扩展版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 配置类
实现步骤
1.项目构建
可基于原有noob-rpc-easy模块进行构建,创建noob-rpc-core模块作为扩展版本的RPC框架实现(后续相关的扩展均基于noob-rpc-core实现),将原有noob-rpc-easy的内容copy过去(相当于基于noob-rpc-easy进行改造)
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);
}
}
测试全局配置对象加载(简易服务提供者示例)
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());
}
}
扩展思路
扩展思路参考
【1】支持读取application.yml、 application yaml等不同格式的配置文件
【2】支持监听配置文件的变更,并自动更新配置对象
参考思路:使用Hutool工具类的props. autol oad()可以实现配置文件变更的监听和自动加载。
【3】配置文件支持中文
参考思路:需要注意编码问题
【4】配置分组:后续随着配置项的增多,可以考虑对配置项进行分组
参考思路:可以通过嵌套配置类实现