2.全局配置加载

全局配置加载

  • 通过读取配置文件来进行加载配置项

执行流程

  • 1.加载全局配置并初始化RPC框架
  • 2.注册服务
  • 3.启动HTTP服务器,等待请求
  • 4.通过加载全局配置并初始化rpc框架获取代理对象并调用代理对象的方法
  • 5.指定序列化器,构造RPC Request
  • 6.将请求序列化为字节数组,发送HTTP请求
  • 7.等待接收HTTP响应
  • 8.通过web服务器监听并接收consumer的请求
  • 9.经过请求处理器将请求反序列化RPC Request
  • 10.使用本地服务注册器查询 provider 中的对应服务并进行反射调用
  • 11.将 provider 返回的结果构造为RPC Response,并将RPC Response序列化为字节数组
  • 12.rpc发送HTTP响应
  • 13.consumer接收响应
  • 14.consumer将响应反序列化为RPC Request
  • 15.consumer获取响应中的结果

全局配置加载

1.项目初始化

1.1 引入依赖

<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.3.12</version>
</dependency>

1.2 修改依赖

1.2.1 修改服务提供者依赖

<dependency>
  <!-- RPC 框架 -->
  <groupId>com.todaysaturday</groupId>
  <artifactId>todaysaturday-rpc-core1.0</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

1.2.2 修改服务消费者依赖

<dependency>
  <!-- RPC 框架 -->
  <groupId>com.todaysaturday</groupId>
  <artifactId>todaysaturday-rpc-core1.0</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

2.配置加载

2.1 创建配置类(RpcConfig)

/**
 * RPC 框架配置
 */
@Data
public class RpcConfig {
    /**
     * 名称
     */
    private String name = "todaysaturday-rpc";

    /**
     * 版本号
     */
    private String version = "1.0";

    /**
     * 服务器主机名
     */
    private String serverHost = "localhost";

    /**
     * 服务器端口号
     */
    private Integer serverPort = 8080;

}

2.2 创建工具类(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>
     */
    private static <T> T loadConfig(Class<T> tClass, String prefix, String environment) {
        StringBuilder configFileBuilder = new StringBuilder("application");
        if(StrUtil.isNotBlank(environment)){
            configFileBuilder.append("-").append(environment);// 追加环境标识,比如dev等
        }

        configFileBuilder.append(".properties");
        // 加载配置文件
        Props props = new Props(configFileBuilder.toString());
        // 解析为Java对象
        T result = props.toBean(tClass, prefix);
        return result;
    }
}

2.3 创建常量类(RpcConstant)

/**
 * Rpc 相关常量
 */
public class RpcConstant {
    /**
     * 默认配置文件加载前缀
     */
    public static final String DEFAULT_CONFIG_PREFIX = "rpc";

}

2.4 创建启动类(RpcApplication)


/**
 * Rpc 框架应用
 * 相当于holder,存放了项目全局用到的变量,双检锁单例模式实现
 * 支持在获取配置时才调用 init 方法实现懒加载。
 */
@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;
    }
}

2.5 创建配置文件

application.propertes

rpc.name=todaysaturdayrpc
rpc.version=2.0
rpc.serverPort=8081

2.6 修改代理实现类

  • 这里如果不修改,就还是走的默认端口8080
// 从配置中取值进行拼接URI
Integer serverPort = RpcApplication.getRpcConfig().getServerPort();
String serverHost = RpcApplication.getRpcConfig().getServerHost();
String url = "http://"+serverHost +":" +serverPort;

// 4.发出HTTP请求
try(HttpResponse httpResponse = HttpRequest.post(url)
        .body(bodyBytes)
        .execute()){
    result = httpResponse.bodyBytes();
}

3.扩展

3.0 新增yaml/yml

application.yml

rpc:
  name: todaysaturdayrpc-yaml
  version: 3.0
  serverPort: 8082

application.yaml

rpc:
  name: todaysaturdayrpc-yaml
  version: 4.0
  serverPort: 8083

3.1 引入依赖

<!--支持解析yaml和yml文件-->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.15.2</version> </dependency>
<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
  <version>2.15.2</version>
</dependency>

3.2 修改RpcConstant

/**
 * 解析 YAML 的 ObjectMapper
 */
public static final com.fasterxml.jackson.databind.ObjectMapper YAML_MAPPER = new com.fasterxml.jackson.databind.ObjectMapper(new YAMLFactory());

3.3 修改ConfigUtils

/**
 * 加载配置对象,支持区分环境,支持yaml和yml文件。
 * @param tClass
 * @param prefix
 * @param environment
 * @return
 * @param <T>
 */
private static <T> T loadConfig(Class<T> tClass, String prefix, String environment,String configFileExtension) {
    StringBuilder configFileBuilder = new StringBuilder("application");
    if(StrUtil.isNotBlank(environment)){
        configFileBuilder.append("-").append(environment);// 追加环境标识,比如dev等
    }
    T result = null;
    if(!StrUtil.isEmpty(configFileExtension)){
        if("yaml".equals(configFileExtension) || "yml".equals(configFileExtension)){
            String fullConfigPath = configFileBuilder.toString();
            // 获取 src/main/resource 路径下的文件
            try(InputStream inputStream = ConfigUtils.class.getClassLoader().getResourceAsStream(fullConfigPath +"."+configFileExtension)){
                if(inputStream !=null){
                    JsonNode rootNode = YAML_MAPPER.readTree(inputStream);
                    JsonNode prefixedNode = rootNode.path(prefix);
                    result = YAML_MAPPER.treeToValue(prefixedNode, tClass);
                    System.out.println(result.toString());
                }
            } catch (Exception e) {
               e.printStackTrace();
            }
        }
    }else{
        configFileBuilder.append(".properties");
        // 加载配置文件
        Props props = new Props(configFileBuilder.toString());
        // 解析为Java对象
        result = props.toBean(tClass, prefix);
    }

    return result;
}

3.4 修改服务提供者

/**
 *  简易服务提供者示例
 */
public class EasyProviderExample {
    public static void main( String[] args ){

        RpcConfig rpcConfig = ConfigUtils.loadConfig(RpcConfig.class, "rpc", "", "yaml");
        // RPC 框架初始化
        RpcApplication.init(rpcConfig);
        System.out.println("RpcProvider:"+RpcApplication.getRpcConfig());

        // 注册服务
        LocalRegistry.register(UserService.class.getName(), UserServiceImpl.class);

        // 启动 web 服务
        VertxHttpServer httpServer = new VertxHttpServer();
        httpServer.doStart(RpcApplication.getRpcConfig().getServerPort());
    }
}

3.5 修改服务消费者

/**
 * 服务消费者启动类
 *
 */
public class EasyConsumerExample {
    public static void main( String[] args ){

        // 加载配置文件
        RpcConfig rpcConfig = ConfigUtils.loadConfig(RpcConfig.class, "rpc", "", "yaml");
        RpcApplication.init(rpcConfig);

        System.out.println("RpcConsumer:"+RpcApplication.getRpcConfig());
        // 创建静态代理
//        UserService userService = new UserServiceProxy();
        // 创建JDK动态代理
        UserService userService = JdkServiceProxyFactory.getProxy(UserService.class);
        // 创建CGlib动态代理
//        UserService userService = CGlibServiceProxyFactory.getProxy(UserService.class);
        User user = new User();
        user.setName("todaysaturday");

        User newUser = userService.getUser(user);
        if(newUser == null){
            System.out.println("user == null");
        }else{
            System.out.println("user == "+ newUser.getName());
        }

    }
}