Ver: 1.0 | Updated: 2022.09.10 | @Zak
ZRPC:一个基于Spring的轻量RPC(Remote Procedure Call)框架,解决多服务之间的轻量远程函数调用问题。(复杂场景建议使用Dubbo, gRPC等其他RPC框架)
- 在Maven Java项目pom.xml中(调用方&提供方都需要添加此依赖)
<dependency> <groupId>cn.pingbase.zrpc</groupId> <artifactId>zrpc-core</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
- 配置(仅需要在调用方配置,提供方不需要加配置)
# 1)简单配置 zrpc: remotes: - serverName: ServerA # 必填! 这个名字下面调用方注解需要用 schema: http # http/https endpoint: 127.0.0.1 # 必填! 目标提供方地址(在k8s环境中可以配置service name) port: 8080 # 必填! 必须和目标提供方的server.port值一致 # 2)多个远程提供方 zrpc: remotes: - serverName: ServerA schema: http endpoint: 127.0.0.1 port: 8080 - serverName: ServerB schema: http endpoint: 127.0.0.1 port: 9090
- 在调用方和提供方的启动类上注解(
@ZRPCPackageScan
非必须,但建议配置上,因为这有助于加快扫描速度以及注册的准确度。)@SpringBootApplication // 必须 @ComponentScan(basePackages = {"cn.pingbase.zrpc"}) // 必须 @ZRPCPackageScan(basePackages = {"com.xxx.yyy"}) // 可选,不填则扫描所有 public class TestServer { public static void main(String[] args) { SpringApplication.run(TestServer.class, args); } }
- 在调用方的接口类(interface)上注解:
// serverName值和serviceIdentifier值必须在服务层面全局唯一。 // serverName值必须和配置中的serverName值一致。 // serviceIdentifier值必须和提供方@ZRPCRemoteService注解的serviceIdentifier值一致。 @ZRPCRemoteClient(serverName = "ServerA", serviceIdentifier = "TestService")
- 在提供方的接口类/实现类(interface/class)上注解:
// serviceIdentifier值必须在服务层面全局唯一。 // serviceIdentifier值必须和调用方@ZRPCRemoteClient注解的serviceIdentifier值一致。 // serviceImplClass: 可选,用于拥有多个实现类时指定RPC调用接口使用哪个具体实现类。 @ZRPCRemoteService(serviceIdentifier = "TestService", serviceImplClass = xxxx.class)
- 调用接口
// (IDEA警告可忽略: "Could not autowire. No beans of 'TestService' type found.") @Autowired TestService testService; // 或使用J2EE的@Resource注入方式,这样不会有IDEA的"Could not autowire"警告 @Resource TestService testService; // 调用接口方法 testService.test();
当远程函数的返回值/参数值为自定义类型,RPC过程中的序列化会是一个问题
ZRPC为了解决这个问题进行了类型自动映射,使用者仅需要在调用方的interface中具体函数上注解@ZRPCSerializeBinder
,例如:
@ZRPCSerializeBinder(remoteClassName = "com.xxx.remote.model.TestModel", currentClass = TestModel.class)
@ZRPCSerializeBinder(remoteClassName = "com.xxx.remote.model.Body", currentClass = Body.class)
List<TestModel> getTestModel(Body body);
也可以使用另外一个注解@ZRPCSerializeBinders
,例如:
@ZRPCSerializeBinders({
@ZRPCSerializeBinder(remoteClassName = "com.xxx.remote.model.TestModel", currentClass = TestModel.class),
@ZRPCSerializeBinder(remoteClassName = "com.xxx.remote.model.Body", currentClass = Body.class)
})
TestModel getTestModel(Body body);
以上两种注解方式都可以实现一样的功能。
- 当远程函数的执行出现业务的异常(逻辑代码主动抛出的异常),默认RPC调用方收到的异常类型为
ZRPCBusinessException
- 但某些场景下RPC调用方需要根据特定异常类型进行逻辑处理,这时仅需在调用方的interface中函数上注解
@ZRPCThrowableBinder
即可自定义业务异常类型 - 异常中的
message
字段已赋值为提供方抛异常时的值,stack信息在这个场景下意义不大,不进行传递。
@ZRPCThrowableBinder(exceptionClass = CustomException.class)
void check();
注意: 自定义的CustomException
必须继承Exception/RuntimeException/Throwable并实现单个String类型入参的构造函数!
ZRPC框架支持调整socket参数,当前默认参数及含义:
# 连接超时时间(单位: 毫秒)
connectTimeoutInMs: 500
# 读超时时间(单位: 毫秒)
readTimeoutInMs: 500
# 写超时时间(单位: 毫秒)
writeTimeoutInMs: 500
# 连接池最大闲置连接数量
maxIdleConnections: 50
# 连接池闲置连接最长保活时间(单位: 分钟)
keepAliveDurationInMin: 5
完整配置示例:
zrpc:
remotes:
- serverName: ServerA
schema: http
endpoint: 127.0.0.1
port: 8080
- serverName: ServerB
schema: http
endpoint: 127.0.0.1
port: 9090
socket:
connectTimeoutInMs: 500
readTimeoutInMs: 500
writeTimeoutInMs: 500
maxIdleConnections: 50
keepAliveDurationInMin: 5
- 英文文档、调用示例程序。
- 提升资源隔离性(独立线程,独立连接),降低某些场景下对业务本身的影响。
- 支持配置多种序列化框架,给用户更多的选择空间。
- 兼容主流注册中心协议,如:Nacos、Consul、Etcd等。
- 支持多种模式:独立模式/分布式注册模式。
- 实现ZRPC注册中心,支持多负载均衡算法与动态流量控制。
- Metrics,可监控:总调用量,服务/接口成功率,延迟等指标。
- 核心调用稳定性、性能报告。