跳至主要內容

扩展版RPC框架-全局配置加载

holic-x...大约 7 分钟项目RPC

扩展版RPC框架-全局配置加载

扩展核心

【1】引入全局配置(框架配置核心构建)

【2】构建RpcApplication加载全局配置

需求分析

​ 在使用一些现有的RPC框架,可能会涉及到一些配置信息,例如注册中心地址、序列化方式、网络服务器端口号等。在基础版RPC框架中这些配置在程序中使用了硬编码实现,反而不利于维护。且RPC框架是需要被其他项目作为服务提供者或者服务消费者引入的,应当允许引入框架的项目通过编写配置文件来定义配置。一般情况下,服务提供者和服务消费者需要编写相同的RPC配置。因此,需要一套全局配置加载功能。能够让RPC框架轻松地从配置文件中读取配置,并且维护一个全局配置对象,便于框架快速获取到一致的配置。

梳理配置项(确认基础配置项,参考现成RPC框架配置)

基础配置项:name名称、version版本号、serverHost服务器主机名、serverPort服务器端口号

后续随着框架功能的扩展,会不断地新增配置项,可以适当地对配置项进行分组

常见RPC框架配置参考:

【1】注册中心地址:服务提供者和服务消费者都需要指定注册中心的地址,以便进行服务的注册和发现

【2】服务接口:服务提供者需要指定提供的服务接口,而服务消费者需要指定要调用的服务接口

【3】序列化方式:服务提供者和服务消费者都需要指定序列化方式,以便在网络中传输数据时进行序列化和反序列化

【4】网络通信协议:服务提供者和服务消费者都需要选择合适的网络通信协议,比如TCP、HTTP等

【5】超时设置:服务提供者和服务消费者都需要设置超时时间,以便在调用服务时进行超时处理

【6】负载均衡策略:服务消费者需要指定负载均衡策略,以决定调用哪个服务提供者实例

【7】服务端线程模型:服务提供者需要指定服务端线程模型,以决定如何处理客户端请求

参考[Dubbo RPC框架的配置项](htps://cn.dubbo apache org/zh-cn/overview/mannual/java- sdk/reference manual/configlapi/),包括应用配置、注册中心配置、服务配置等,可以在任意项目引入Dubbo依赖查看到ApplicationConfig 配置类

image-20240413183859919

实现步骤

1.项目构建

​ 可基于原有noob-rpc-easy模块进行构建,创建noob-rpc-core模块作为扩展版本的RPC框架实现(后续相关的扩展均基于noob-rpc-core实现),将原有noob-rpc-easy的内容copy过去(相当于基于noob-rpc-easy进行改造)

image-20240413184312735

pom.xml参考(按需引入依赖,此处引入日志库和单元测试依赖)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.noob.rpc</groupId>
    <artifactId>noob-rpc-core</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/io.vertx/vertx-core -->
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-core</artifactId>
            <version>4.5.1</version>
        </dependency>

        <!-- https://doc.hutool.cn/ -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.16</version>
        </dependency>

        <!-- https://projectlombok.org/ -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>

        <!-- 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>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

sample-consumer、sample-provider中的依赖调整

​ 将sample-consumer、sample-provider原有对noon-rpc-easy的依赖引入,调整为对noob-rpc-core的引入(内容调调是一样的)

			 <!-- 引入简易版RPC框架 -->
        <!--
        <dependency>
            <groupId>com.noob.rpc</groupId>
            <artifactId>noob-rpc-easy</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        -->

        <!-- 引入扩展版PRC框架 -->
        <dependency>
            <groupId>com.noob.rpc</groupId>
            <artifactId>noob-rpc-core</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

2.配置加载

构建步骤说明

【1】在config包下创建配置类ReqConfig(保存配置信息)

【2】在utils包下创建ConfigUtils工具类(读取配置文件并返回配置对象,简化调用)

【3】在constant包下创建RpcConstant接口,存储RPC框架相关常量

ReqConfig

/**
 * RPC 框架全局配置
 */
@Data
public class RpcConfig {

    /**
     * 名称
     */
    private String name = "noob-rpc";

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

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

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

}

ConfigUtils

​ 通过ConfigUtils的静态方法可以读取相关配置信息

/**
 * 配置工具类
 */
public class ConfigUtils {

    /**
     * 加载配置对象
     * @param tClass
     * @param prefix
     * @param <T>
     * @return
     */
    public static <T> T loadConfig(Class<T> tClass, String prefix) {
        return loadConfig(tClass, prefix, "");
    }

    /**
     * 加载配置对象,支持区分环境
     * @param tClass
     * @param prefix
     * @param environment
     * @param <T>
     * @return
     */
    public static <T> T loadConfig(Class<T> tClass, String prefix, String environment) {
        StringBuilder configFileBuilder = new StringBuilder("application");
        if (StrUtil.isNotBlank(environment)) {
            configFileBuilder.append("-").append(environment);
        }
        configFileBuilder.append(".properties");
        Props props = new Props(configFileBuilder.toString());
        return props.toBean(tClass, prefix);
    }
}

RpcConstant

/**
 * RPC 相关常量
 */
public interface RpcConstant {

    /**
     * 默认配置文件加载前缀
     */
    String DEFAULT_CONFIG_PREFIX = "rpc";

    /**
     * 默认服务版本
     */
    String DEFAULT_SERVICE_VERSION = "1.0";

}

// 默认配置文件加载前缀:可设定读取到类似下列配置
rpc.name=noob-rpc
rpc.version=2.0
rpc.serverPort=8080

3.维护全局配置对象

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

​ 使用设计模式中的单例模式,就能够很轻松地实现这个需求了。一般情况下,使用holder来维护全局配置对象实例。项目中,可以使用RpcApplication类作为RPC项目的启动入口、并且维护项目全局用到的变量。

RpcApplication

​ 双检锁单例模式实现:支持在获取配置时才调用init方法实现懒加载。可支持传入配置对象,如果不传入默认引入ConfigUtils来加载配置,基于这种设计只需要一行代码即可加载配置

RpcConfig rpc = RpcApplication.getRpcConfig();
/**
 * RPC 框架应用
 * 相当于 holder,存放了项目全局用到的变量。双检锁单例模式实现
 */
@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;
    }
}

4.测试

​ 在sample-consumer项目的resource文件夹下编写配置文件application.properties

rpc.name=noob-rpc
rpc.version=2.0
rpc.serverPort=8081

编写代码测试文件读取(简易服务消费者示例)

public class Main {
    public static void main(String[] args) {
        // 测试配置文件读取
        RpcConfig rpc = RpcApplication.getRpcConfig();
        System.out.println(rpc);
    }
}

image-20240413192036169

测试全局配置对象加载(简易服务提供者示例)

public class Main {
    public static void main(String[] args) {

        // 框架初始化
        RpcApplication.init();

        System.out.println("Hello world!");// 提供服务
        LocalRegistry.register(UserService.class.getName(), UserServiceImpl.class);

        // 启动web服务
        HttpServer httpServer = new VertxHttpServer();
        // 启动web服务(从RPC框架中的全局配置中获取端口)
        httpServer.doStart(RpcApplication.getRpcConfig().getServerPort());

    }
}

image-20240413192523563

扩展思路

扩展思路参考

【1】支持读取application.yml、 application yaml等不同格式的配置文件

【2】支持监听配置文件的变更,并自动更新配置对象

​ 参考思路:使用Hutool工具类的props. autol oad()可以实现配置文件变更的监听和自动加载。

【3】配置文件支持中文

​ 参考思路:需要注意编码问题

【4】配置分组:后续随着配置项的增多,可以考虑对配置项进行分组

​ 参考思路:可以通过嵌套配置类实现

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3