一、概述
手動(dòng)實(shí)現(xiàn)一款輕量嚣伐,高效的RPC框架哭廉,基于TCP的二進(jìn)制協(xié)議實(shí)現(xiàn)
github源碼:https://github.com/wosn00/srpc
二怀跛、特征
- 基于netty的主從Reactor模型距贷,NIO通信
- 支持同步,異步吻谋,攜帶回調(diào)等調(diào)用方式
- 支持spring項(xiàng)目下引入starter包開箱即用忠蝗,整合spring,實(shí)現(xiàn)服務(wù)接口透明使用
- 支持非spring項(xiàng)目下單獨(dú)使用漓拾,可不依賴spring環(huán)境
- 支持多種序列化類型阁最,Protostuff,Kryo骇两,Json速种,Jdk等
- 支持多種壓縮算法,Snappy低千,Lz4配阵,gzip,bzip2示血,Deflate棋傍,Lzo等
- 支持注冊(cè)中心,自動(dòng)服務(wù)注冊(cè)和發(fā)現(xiàn)难审,默認(rèn)實(shí)現(xiàn)zookpeer瘫拣,也可不使用注冊(cè)中心,手動(dòng)指定服務(wù)端節(jié)點(diǎn)地址列表
- 支持多種負(fù)載均衡策略剔宪,隨機(jī)拂铡,輪詢,一致性hash等
- 支持服務(wù)容錯(cuò)葱绒,連接/調(diào)用異常情況下自動(dòng)排除服務(wù)端故障節(jié)點(diǎn)
- 支持SPI擴(kuò)展點(diǎn),可擴(kuò)展負(fù)載均衡策略斗锭,壓縮算法地淀,序列化類型,線程池岖是,注冊(cè)中心等
- 支持TLS雙向認(rèn)證加密
- 支持流量整形帮毁,請(qǐng)求異常重試,服務(wù)端請(qǐng)求去重等功能
三豺撑、設(shè)計(jì)
可能對(duì)RPC框架性能產(chǎn)生影響的幾個(gè)因素:
- 網(wǎng)絡(luò)IO線程模型
- 通信協(xié)議設(shè)計(jì)
- 序列化性能
- 服務(wù)調(diào)用管理方式
- 連接池的維護(hù)
3.1 RPC協(xié)議
協(xié)議上設(shè)計(jì)盡量緊湊烈疚,4位bit用于標(biāo)識(shí)序列化類型,壓縮類型和指令類型聪轿,方法映射上不需要像dubbo那樣傳輸完整的類 方法 參數(shù)信息導(dǎo)致的無用流量增大爷肝,而是自行生成對(duì)應(yīng)rpc調(diào)用方法的唯一標(biāo)識(shí)字符串
3.2 同步線程模型
3.3 異步線程模型
四、使用示例
4.1、spring環(huán)境下
maven依賴
<dependency>
<groupId>com.hex</groupId>
<artifactId>srpc-spring-boot-starter</artifactId>
<version>1.1.0</version>
</dependency>
若需使用zookeeper作為注冊(cè)中心則引入
<dependency>
<groupId>com.hex</groupId>
<artifactId>srpc-registry-zookeeper</artifactId>
<version>1.1.0</version>
</dependency>
server端使用
1.定義服務(wù)接口
@SRpcClient(serviceName = "testService")
public interface HelloService {
String hello(String name);
}
接口添加@SRpcClient注解灯抛,serviceName屬性為rpc服務(wù)都在注冊(cè)中心的服務(wù)名稱金赦,若不使用注冊(cè)中心,則注解nodes屬性需手動(dòng)指定服務(wù)端節(jié)點(diǎn)集群地址对嚼,將根據(jù)負(fù)載均衡策略自動(dòng)選取節(jié)點(diǎn)調(diào)用
@SRpcClient(nodes = {"127.0.0.1:9955","127.0.0.1:9956","127.0.0.1:9957"})
public interface HelloService {
String hello(String name);
}
2.服務(wù)接口實(shí)現(xiàn)
@SRpcRoute
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return name + " Hey bro, it's a good day";
}
}
實(shí)現(xiàn)類添加@SRpcRoute注解夹抗,便會(huì)自動(dòng)注冊(cè)為spring的單例bean,可視為等同@Comphonent使用纵竖,內(nèi)部可用@Autowired等spring相關(guān)注解漠烧,也可被其他bean注入。
3.配置yml
因同時(shí)包含了rpc客戶端和服務(wù)端靡砌,所以客戶端和服務(wù)端都需要配置沽甥,如需個(gè)性化配置的地方在yml或properties文件按需配置即可,以srpc.server或srpc.client為前綴乏奥。所有可自由配置的選項(xiàng)如下
服務(wù)端默認(rèn)配置:
@ConfigurationProperties(prefix = "srpc.server")
public class RpcServerProperties {
private Integer port = 9957; //綁定端口
private Integer businessThreads = 200; //業(yè)務(wù)處理線程池大小摆舟,0為不設(shè)置
private Integer businessQueueSize = 500; //業(yè)務(wù)線程池隊(duì)列大小
private Integer connectionIdleTime = 180;//超過連接空閑時(shí)間(秒)未收發(fā)數(shù)據(jù)則關(guān)閉連接
private Integer printConnectionNumInterval = 0; //打印服務(wù)端當(dāng)前連接詳情, 時(shí)間間隔(秒), 0為不打印
private Boolean isPrintHearBeatPacketInfo = false; //是否打印心跳包信息
private CompressType compressType = CompressType.SNAPPY; //壓縮算法類型,無需壓縮為NONE
private SerializeType serializeType = SerializeType.PROTOSTUFF; //序列化類型邓了,默認(rèn)protostuff
private Integer sendBuf = 65535; //tcp發(fā)送緩沖區(qū)
private Integer receiveBuf = 65535; //tcp接收緩沖區(qū)
private Integer lowWaterLevel = 1024 * 1024; //netty低水位
private Integer highWaterLevel = 10 * 1024 * 1024; //netty高水位
private boolean deDuplicateEnable = false; //是否開啟去重處理
private Integer duplicateCheckTime = 10; //請(qǐng)求去重緩存時(shí)長(秒)
private Long duplicateMaxSize = 1024 * 64L; //最大緩存請(qǐng)求個(gè)數(shù)
private Boolean trafficMonitorEnable = false; //是否開啟流控
private Long maxReadSpeed = 10 * 1000 * 1000L; //帶寬限制恨诱,最大讀取速度
private Long maxWriteSpeed = 10 * 1000 * 1000L; //帶寬限制,最大寫出速度
// ----tls加密部分配置
private Boolean useTLS = false; //是否開啟tls加密
private String keyPath; //私鑰文件路徑
private String keyPwd; //密碼
private String certPath; //證書文件路徑
private String trustCertPath; //受信任ca證書路徑
private String clientAuth; //是否要求客戶端認(rèn)證
// ----注冊(cè)中心配置部分
private Boolean enableRegistry = false; //是否使用注冊(cè)中心
private String registrySchema; //注冊(cè)中心模式名稱
private List<String> registryAddress; //注冊(cè)中心地址
客戶端默認(rèn)配置:
@ConfigurationProperties(prefix = "srpc.client")
public class RpcClientProperties {
private Integer callBackTaskThreads = 200; //回調(diào)任務(wù)處理線程池大小骗炉,0為不設(shè)置
private Integer callBackTaskQueueSize = 500; //回調(diào)任務(wù)線程池隊(duì)列大小
private Integer connectionTimeout = 5; //連接超時(shí)時(shí)間(秒)
private Integer requestTimeout = 10; //請(qǐng)求超時(shí)時(shí)間(秒)
private Integer connectionSizePerNode = 3; //每個(gè)節(jié)點(diǎn)連接數(shù)
private Integer connectionIdleTime = 180; //超過連接空閑時(shí)間(秒)未收發(fā)數(shù)據(jù)則關(guān)閉連接
private Integer heartBeatTimeInterval = 30; //發(fā)送心跳包間隔時(shí)間(秒)
private CompressType compressType = CompressType.SNAPPY; //壓縮算法類型照宝,無需壓縮為NONE
private SerializeType serializeType = SerializeType.PROTOSTUFF; //序列化類型,默認(rèn)protostuff
private LoadBalanceRule loadBalanceRule = LoadBalanceRule.RANDOM; //集群負(fù)載均衡策略
private boolean excludeUnAvailableNodesEnable = true; //集群模式下是否排除不可用的節(jié)點(diǎn)
private Integer nodeErrorTimes = 3; //節(jié)點(diǎn)連接或請(qǐng)求超時(shí)/異常超過設(shè)置次數(shù)則置為節(jié)點(diǎn)不可用
private Integer nodeHealthCheckTimeInterval = 10; //節(jié)點(diǎn)健康檢查周期(秒),心跳包響應(yīng)成功則恢復(fù)不可用的節(jié)點(diǎn)
private Integer sendBuf = 65535; //tcp發(fā)送緩沖區(qū)
private Integer receiveBuf = 65535; //tcp接收緩沖區(qū)
private Integer lowWaterLevel = 1024 * 1024; //netty低水位
private Integer highWaterLevel = 10 * 1024 * 1024; //netty高水位
private Boolean trafficMonitorEnable = false; //是否開啟流量控制
private Long maxReadSpeed = 10 * 1000 * 1000L; //帶寬限制句葵,最大讀取速度
private Long maxWriteSpeed = 10 * 1000 * 1000L; //帶寬限制厕鹃,最大寫出速度
// ----TLS加密部分配置
private Boolean useTLS = false; //是否開啟TLS加密
private String keyPath; //私鑰文件路徑
private String keyPwd; //密碼
private String certPath; //證書文件路徑
private String trustCertPath; //受信任ca證書路徑
private String clientAuth; //是否要求客戶端認(rèn)證
// ----注冊(cè)中心配置部分
private Boolean enableRegistry = false; //是否使用注冊(cè)中心
private String registrySchema; //注冊(cè)中心模式名稱, 缺省為zookeeper
private List<String> registryAddress; //注冊(cè)中心地址
配置類信息:
https://github.com/wosn00/srpc/blob/master/srpc-spring-boot-starter/src/main/java/com/hex/rpc/spring/starter/properties/RpcClientProperties.java
4.服務(wù)端啟動(dòng)
@SpringBootApplication
@EnableSRpc(basePackages = "com.hex.example.provider")
public class RpcTestApplication {
public static void main(String[] args) {
SpringApplication.run(RpcTestApplication.class, args);
}
}
啟動(dòng)類上添加@EnableSRpc注解,basePackages為需要掃描的包路徑乍丈,包含@SRpcClient和@SRpcRoute注解的包路徑剂碴,相應(yīng)的類都會(huì)被自動(dòng)注冊(cè)為spring的單例bean,缺省為啟動(dòng)類上級(jí)包路徑
client端使用
1.服務(wù)接口調(diào)用
@Component
public class HelloRpcTest {
@Autowired
private HelloService helloService; // 上面定義的rpc服務(wù)接口
public void rpcServerTest(String name) {
String msg = helloService.hello(name);
System.out.println(msg);
}
}
上述服務(wù)端定義的帶有@SRpcClient注解的rpc服務(wù)接口轻专,使用spring的@Autowired注入即可遠(yuǎn)程調(diào)用
2.配置yml(同上)
3.客戶端啟動(dòng)(同上)
4.2忆矛、非spring環(huán)境下
maven依賴
<dependency>
<groupId>com.hex</groupId>
<artifactId>srpc-core</artifactId>
<version>1.1.0</version>
</dependency>
若需使用zookeeper作為注冊(cè)中心則引入
<dependency>
<groupId>com.hex</groupId>
<artifactId>srpc-registry-zookeeper</artifactId>
<version>1.1.0</version>
</dependency>
server端使用
1.定義服務(wù)接口實(shí)現(xiàn)
@SRpcRoute
public class HelloServiceImpl {
@Mapping("hello")
public String hello(String name) {
return name + " Hey bro, it's a good day";
}
}
2.服務(wù)端啟動(dòng)
@SRpcScan("com.hex.example")
public class ServerTest {
public static void main(String[] args) {
// 啟動(dòng)服務(wù)端, 需填入rpc服務(wù)端配置, 可使用默認(rèn)配置, source填寫有@RouteScan注解的類
SRpcServer.builder()
.serverConfig(new SRpcServerConfig()) //包含rpc服務(wù)端的各項(xiàng)默認(rèn)配置,可自行修改
.sourceClass(ServerTest.class) //有@RouteScan注解的類
.port(8005) //rpc服務(wù)端綁定的端口请垛,默認(rèn)9957
.start();
}
}
啟動(dòng)類上添加@SRpcScan注解催训,值需填寫包含@SRpcRoute注解的類的包路徑,缺省為啟動(dòng)類的上級(jí)包路徑宗收,即可自動(dòng)掃描
client端使用
1.客戶端啟動(dòng)和服務(wù)接口調(diào)用
public class ClientTest {
public static void main(String[] args1) {
// 初始化客戶端漫拭,需填入rpc客戶端配置,可使用默認(rèn)配置
Client rpcClient = SRpcClient.builder()
.config(new SRpcClientConfig())
.start();
Object[] args = {"Jack"};
HostAndPort node = HostAndPort.from("127.0.0.1:8005");
// 同步發(fā)送請(qǐng)求混稽,獲取響應(yīng)
String response = rpcClient.invoke("hello", String.class, args, node);
System.out.println(response);
// 異步發(fā)送請(qǐng)求采驻,發(fā)送完成即返回审胚,不阻塞等待響應(yīng)結(jié)果
rpcClient.invokeAsync("hello",
rpcResponse -> System.out.println("收到響應(yīng),開始執(zhí)行回調(diào)方法" + rpcResponse), args, node);
}
}
Client更多調(diào)用接口及參數(shù)可查看接口說明:
https://github.com/wosn00/srpc/blob/master/srpc-core/src/main/java/com/hex/srpc/core/rpc/Client.java
五挑宠、性能測(cè)試
5.1 與dubbo的性能對(duì)比測(cè)試
目前只是與dubbo進(jìn)行了簡單了的性能測(cè)試對(duì)比 0_0菲盾,后續(xù)有時(shí)間會(huì)進(jìn)行更多的測(cè)試
條件:
1.測(cè)試相同接口模擬業(yè)務(wù)處理延遲30ms后返回
2.服務(wù)端業(yè)務(wù)處理線程池均為500
3.dubbo采用默認(rèn)的dubbo協(xié)議,srpc使用protostuff序列化