2、全局配置加载

1355字约5分钟

2024-05-18

全局配置加载

一、需求分析

​ 在 RPC 框架运行的过程中,会涉及到很多的配置信息,比如注册中心的地址、序列化方式、网络服务器端口号等等。

​ 之前的简易版 RPC 项目中,对这些配置进行硬编码,不利于维护。

​ 而且 RPC 框架是需要被其他项目作为服务提供者或者服务消费者引入的,我们应当允许引入框架的项目通过编写配置文件来自定义配置。并且一般情况下,服务提供者和服务消费者需要编写相同的 RPC 配置。

​ 因此,需要实现一套全局配置加载功能。能够让 RPC 框架轻松地从配置文件中读取配置,并且维护一个全局配置对象,便于框架快速获取到一致的配置。

二、设计方案

1、配置项

常见的 RPC 框架配置项如下:

  1. 注册中心地址:服务提供者和服务消费者都需要指定注册中心的地址,以便进行服务的注册和发现。
  2. 服务接口:服务提供者需要指定提供的服务接口,而服务消费者需要指定要调用的服务接口。
  3. 序列化方式:服务提供者和服务消费者都需要指定序列化方式,以便在网络中传输数据时进行序列化和反序列化。
  4. 网络通信协议:服务提供者和服务消费者都需要选择合适的网络通信协议,比如TCP、HTTP 等。
  5. 超时设置:服务提供者和服务消费者都需要设置超时时间,以便在调用服务时进行超时处理。
  6. 负载均衡策略:服务消费者需要指定负载均衡策略,以决定调用哪个服务提供者实例。
  7. 服务端线程模型:服务提供者需要指定服务端线程模型,以决定如何处理客户端请求。

目前只使用以下几个配置:

  1. name 名称
  2. version 版本号
  3. serverHost 服务器主机名
  4. serverPort 服务器端口号

2、读取配置文件

​ 读取配置文件可以使用 Java的 Properties 类自行编写,但是更推荐使用一些第三方工具库,比如 Hutool 的 Setting 模块,可以直接读取指定名称的配置文件中的部分配置信息,并且转换成 Java 对象,非常方便。

三、开发实现

1、项目初始化

  1. 新建 core-rpc 模块
  2. 给项目引入日志库和单元测试依赖,便于后续开发
  3. 给 consumer 和 provider 引入的 easy-rpc模块换为 core-rpc 模块

2、配置加载

  1. 在 config 包下新建配置类 Rpcconfig ,用于保存配置信息。

    @Data
    public class RpcConfig {
    
        /**
         * 名称
         */
        private String name = "honghu-rpc";
        /**
         * 版本号
         */
        private String version = "1.0";
        /**
         * 服务器主机名
         */
        private String serverHost = "localhost";
        /**
         * 服务器端口号
         */
        private Integer serverPort = 8080;
    }
  2. 在 utis 包下新建工具类 configutils ,作用是读取配置文件并返回配置对象,可以简化调用,工具类应当尽量通用,和业务不强绑定,提高使用的灵活性。比如支持外层传入要读取的配置内容前缀、支持传入环境等。

    public class ConfigUtils {
    
        /**
         * 加载配置对象
         *
         * @param tClass
         * @param prefix
         * @return
         * @param <T>
         */
        public static <T> T loadConfig(Class<T> tClass, String prefix){
            return loadConfig(tClass,prefix,"");
        }
    
        /**
         * 加载配置对象,支持区分环境
         *
         * @param tClass
         * @param prefix
         * @param environment
         * @return
         * @param <T>
         */
        public static <T> T loadConfig(Class<T> tClass, String prefix,String environment){
            StringBuilder configFileBulider = new StringBuilder("application");
            if(StrUtil.isNotBlank(environment)){
                configFileBulider.append("-").append(environment);
            }
            configFileBulider.append(".properties");
            Props props = new Props(configFileBulider.toString());
            return props.toBean(tClass,prefix);
        }
    }
  3. 在 constant 包中新建 Rpcconstant 接囗,用于存储 RPC 框架相关的常量

    public interface RpcConstant {
    
        /**
         * 默认配置文件加载前缀
         */
        String DEFAULT_CONFIG_PREFIX = "rpc";
    }

3、维护全局配置对象

​ RPC框架中雲要维护一个全局的配置对象。在引入 RPC 框架的项目启动时,从配置文件中读取配置并创建对象实例,之后就可以集中地从这个对象中获取配置信息,而不用每次加载配置时再重新读取配置、并创建新的对象,减少了性能开销。

​ 使用设计模式中的 单例模式,就能够很轻松地实现这个需求。

  1. 使用RpcApplication 类作为 RPC 项目的启动入口、并且维护项目全局用到的变量。

    @Slf4j
    public class RpcApplication {
    
        private static volatile RpcConfig rpcConfig;
    
        /**
         * 框架初始化,支持传入自定义配置
         *
         * @param newRpcConfig
         */
        public static void init(RpcConfig newRpcConfig){
            rpcConfig = newRpcConfig;
            log.info("rpc init, config = {}" ,newRpcConfig.toString());
        }
    
        /**
         * 初始化
         */
        public static void init(){
            RpcConfig newRpcConfig;
            try{
                newRpcConfig = ConfigUtils.loadConfig(RpcConfig.class,RpcConstant.DEFAULT_CONFIG_PREFIX);
            }catch (Exception e){
                //配置加载失败,使用默认值
                newRpcConfig = new RpcConfig();
            }
            init(newRpcConfig);
        }
    
        /**
         * 获取配置
         *
         * @return
         */
        public static RpcConfig getRpcConfig(){
            if(rpcConfig == null){
                synchronized (RpcApplication.class){
                    if(rpcConfig == null) {
                        init();
                    }
                }
            }
            return rpcConfig;
        }
    }

    ​ 这里采用了双检锁单例模式,支持在获取配置时才调用 init 方法实现懒加载。为了便于扩展,还支持自己传入配置对象;如果不传入,则默认调用前面写好的 Configutils 来加载配置。以后 RPC 框架内只需要写一行代码,就能正确加载到配置:

    RpcConfig rpcConfig = RpcApplication.getRpcConfig();