# koalas-rpc
**Repository Path**: yk.git/koalas-rpc
## Basic Information
- **Project Name**: koalas-rpc
- **Description**: koalas-RPC 个人作品,提供大家交流学习,有意见请私信,欢迎拍砖。客户端采用thrift协议,服务端支持netty和thrift的TThreadedSelectorServer半同步半异步线程模型,支持动态扩容,服务上下线,(权重动态,可用性配置,页面流量统计等,2.0版本)持续为个人以及中小型公司提供可靠的RPC框架技术方案
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 786
- **Created**: 2019-03-27
- **Last Updated**: 2021-10-01
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# koalas-rpc
#### 项目介绍
koalas-RPC
个人作品,提供大家交流学习,有意见请私信,欢迎拍砖。客户端采用thrift协议,服务端支持netty和thrift的TThreadedSelectorServer半同步半异步线程模型,支持动态扩容,服务上下线,(权重动态,可用性配置,页面流量统计等,2.0版本)持续为个人以及中小型公司提供可靠的RPC框架技术方案。
#### 为什么要写这个RPC
市面上常见的RPC框架很多,grpc,motan,dubbo等,但是随着越来越多的元素加入,复杂的架构设计等因素似使得这些框架和spring一样,虽然号称是轻量级,但是用起来却是让我们很蹩脚,大量的配置,繁杂的API设计,其实,我们根本用不上这些东西!!!
我也算得上是在很多个互联网企业厮杀过,见过很多很多的内部RPC框架,有些优秀的设计让我非常赞赏,有一天我突然想着,为什么不对这些设计原型进行聚合归类,自己搞一套【轻量级】RPC框架呢,碍于工作原因,一直没有时间倒腾出空,十一期间工作闲暇,说搞就搞吧,希望源码对大家对认识RPC框架起到推进的作用。东西越写越多,有各种问题欢迎随时拍砖
#### 为什么叫koalas
树袋熊英文翻译,懒的代表,希望考拉RPC给那些不太喜欢动手自己去造轮子的人提供可靠的RPC使用环境
#### 软件架构组成元素
libthrift,spring-core,spring-context,spring-beans,log4j,slf4j,org.apache.commons,io.netty,fastjson,zookeeper
客户端
spring,apache pool,thrift等
服务端
spring,apache pool,thrift,netty等
#### 关于技术选型
1:序列化篇
考察了很多个序列化组件,其中包括jdk原生,kryo、hessian、protostuff,thrift,json等,最终选择了Thrift,原因如下
原生JDK序列化反序列化效率堪忧,其序列化内容太过全面kryo和hessian,json相对来说比原生JDK强一些,但是对跨语言支持一般,所以舍弃了,最终想在protobuf和Thrift协议里面选择一套框架,这俩框架很相通,支持跨语言,需要静态编译等等。但是protobuf不带RPC服务,本着提供多套服务端模式(thrift rpc,netty)的情况下,最终选择了Thrift协议。
2:IO线程模型篇
原生socket可以模拟出简单的RPC框架,但是对于大规模并发,要求吞吐量的系统来说,也就算得上是一个demo级别的,所以BIO肯定是不考虑了,NIO的模型在序列化技术选型的时候已经说了,Thrift本身支持很多个io线程模型,同步,异步,半同步异步等(SimpleServer,TNonblockingServer,THsHaServer,TThreadedSelectorServer,TThreadPoolServer),其中吞吐量最高的肯定是半同步半异步的IO模TThreadedSelectorServer了,具体原因大家可自行google,这次不做多的阐述,选择好了模型之后,发现thrift简直就是神器一样的存在,再一想,对于服务端来说,IO模型怎么能少得了Netty啊,所以下决心也要支持Netty,但是很遗憾Netty目前没有对Thrift的序列化解析,拆包粘包的处理,但是有protobuf,和http协议的封装,怎么办,自己在netty上写对thrift的支持呗,虽然工作量大了一些,但是一想netty不就是干这个事儿的嘛- -!
3:服务发现
支持集群的RPC框架里面,像dubbo,或者是其他三方框架,对服务发现都进行的封装,那么自研RPC的话,服务发现就要自己来写了,那么简单小巧容易上手的zookeeper肯定是首选了。
到目前为止 序列化+IO模型+高可用服务发现机制的技术选型已经落实,也就是RPC性能三大要素:序列化+IO线程模型+高可用服务发现集群。
#### 安装教程
考拉RPC确保精简,轻量的原则,只需要zk服务器进行服务发现(后续版本服务治理可能需要Datasource),对于zookeeper的各个环境安装教程请自行google,不在本安装教程内特意说明
# 使用说明
服务端和客户端都需要引入考拉RPC服务(目前还没有上传到码云,阿里云,和maven中央镜像,本地下载源码后clean install到本地仓库使用即可,2.0版本功能全面之后会统一上传)
koalas.rpc
com.Koalas.rpc
Koalas-1.0-SNAPSHOT
*
*
#### 1:客户端使用方式
#### xml
以下是最精简配置 zkPath为zookeeper的地址,集群环境请用逗号分隔 【127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183】
```
package thrift.service;
import org.apache.thrift.TException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import thrift.domain.WmCreateAccountRequest;
import thrift.domain.WmCreateAccountRespone;
@Service("testService")
public class TestService {
@Autowired
WmCreateAccountService.Iface wmCreateAccountService;
public void getRemoteRpc() throws TException {
WmCreateAccountRequest request= new WmCreateAccountRequest ( );
//request.setSource ( 10 );
request.setAccountType ( 1 );
request.setPartnerId ( 1 );
request.setPartnerType ( 1 );
request.setPartnerName ( "你好" );
request.setPoiFlag ( 1 );
WmCreateAccountRespone respone = wmCreateAccountService.getRPC ( request);
System.out.println (respone);
}
}
```
在你的服务类里面对服务类进行注入就可以了,注意是xxxx.iface。
#### 异步
```
package thrift.service;
import client.async.KoalasAsyncCallBack;
import org.apache.thrift.TException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import thrift.domain.WmCreateAccountRequest;
import thrift.domain.WmCreateAccountRespone;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@Service("testService")
public class TestService2 {
@Autowired
WmCreateAccountService.AsyncIface wmCreateAccountService;
public void getRemoteRpc() throws TException{
KoalasAsyncCallBack
koalasAsyncCallBack = new KoalasAsyncCallBack<> ();
WmCreateAccountRequest request= new WmCreateAccountRequest ( );
//request.setSource ( 10 );
request.setAccountType ( 1 );
request.setPartnerId ( 1 );
request.setPartnerType ( 1 );
request.setPartnerName ( "你好啊" );
request.setPoiFlag ( 1 );
wmCreateAccountService.getRPC ( request ,koalasAsyncCallBack);
Future future= koalasAsyncCallBack.getFuture ();
try {
System.out.println (future.get ());
} catch (InterruptedException e) {
e.printStackTrace ();
} catch (ExecutionException e) {
e.printStackTrace ();
}
}
}
```
在你的服务类里面对服务类进行注入就可以了,注意是xxxx.AsyncIface。
KoalasAsyncCallBack为我为大家写的统一callback方法,支持future接口,并且支持future.get ()同步获取和future.get(long timeout, TimeUnit unit)超时获取两种方式,推荐大家使用。或者你可以自定义你自己的AsyncMethodCallback,具体实现方法参照Thrift官网。
值得说明的是 KoalasAsyncCallBack泛型类型一共有两个参数,第一个参数是方法返回类型,第二个是thrift自动生成xxxxxx_call,和原生callback接口一致
#### 注解
```
```
同步
```
@Service("testServiceSync")
public class TestServiceSync {
@KoalasClient(zkPath = "127.0.0.1:2181",readTimeout = 5000*1000)
WmCreateAccountService.Iface wmCreateAccountService;
public void getRemoteRpc() throws TException {
WmCreateAccountRequest request= new WmCreateAccountRequest ( );
//request.setSource ( 10 );
request.setAccountType ( 1 );
request.setPartnerId ( 1 );
request.setPartnerType ( 1 );
request.setPartnerName ( "你好啊-我是注解实现的" );
request.setPoiFlag ( 1 );
WmCreateAccountRespone respone = wmCreateAccountService.getRPC ( request);
System.out.println (respone);
}
}
```
异步
```
@Service("testServiceAsync")
public class TestServiceAsync {
@KoalasClient(zkPath = "127.0.0.1:2181",readTimeout = 5000*1000)
WmCreateAccountService.AsyncIface wmCreateAccountService;
public void getRemoteRpc() throws TException{
KoalasAsyncCallBack koalasAsyncCallBack = new KoalasAsyncCallBack<> ();
WmCreateAccountRequest request= new WmCreateAccountRequest ( );
//request.setSource ( 10 );
request.setAccountType ( 1 );
request.setPartnerId ( 1 );
request.setPartnerType ( 1 );
request.setPartnerName ( "你好啊-我是注解实现的" );
request.setPoiFlag ( 1 );
wmCreateAccountService.getRPC ( request ,koalasAsyncCallBack);
Future future= koalasAsyncCallBack.getFuture ();
try {
System.out.println (future.get ());
} catch (InterruptedException e) {
e.printStackTrace ();
} catch (ExecutionException e) {
e.printStackTrace ();
}
}
}
```
#### 2:服务端使用方式
#### xml
```
```
serviceInterface和客户端一样是thrift生成的服务类。
serviceImpl是服务类实现。
port是服务类暴露端口,可以自定义设置,同一台机器上不重复即可,客户端会根据serviceInterface自动查找。
zkpath注册的zookeeper的地址。
wmCreateAccountServiceImpl简单实现类的截图如下

实现WmCreateAccountService.Iface接口即可。
#### 注解
```
```
其中package为扫描的路径多个路径用,分隔。为空时默认为spring的扫描路径
```
package thrift.annotation.server.impl;
import annotation.KoalasServer;
import org.apache.thrift.TException;
import thrift.domain.WmCreateAccountRequest;
import thrift.domain.WmCreateAccountRespone;
import thrift.service.WmCreateAccountService;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
@KoalasServer ( port = 8801,zkpath="127.0.0.1:2181")
public class WmCreateAccountServiceNettyImpl implements WmCreateAccountService.Iface {
private AtomicInteger atomicInteger = new AtomicInteger ( 0 );
@Override
public WmCreateAccountRespone getRPC(WmCreateAccountRequest wmCreateAccountRequest) throws TException {
WmCreateAccountRespone wmCreateAccountRespone = new WmCreateAccountRespone ();
wmCreateAccountRespone.setCode ( 1 );
wmCreateAccountRespone.setMessage ( "你好啊" );
if(new Random ( ).nextInt ( 5 )>100){
try {
Thread.sleep ( 5000 );
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
System.out.println ( "getRPC start ...." + wmCreateAccountRequest + "------" + atomicInteger.incrementAndGet () );
return wmCreateAccountRespone;
}
}
```
#### 3:所有参数配置说明

#### 4:客户端服务端RSA双向加密
源码中utils.KoalasRsaUtil的main方法已经为大家写好生成私钥和公钥的代码,执行即可

自动生成4个很长的字符串
将前两个字符串放进client中,后面两个字符串放进server中,依次对应privateKey和publicKey,按照1,2,3,4傻瓜式复制即可:如图


当其中一方的RSA秘钥无法对应,请求会报错。

此时客户端会返回null
RSA对称加密适合给三方系统进行调用,对称加密会影响传输性能。
# 实际性能压测
8C 16G mac开发本,单机10000次请求耗时截图

10w次请求,大约耗时12s,平均qps在8000左右,在集群环境下会有不错的性能表现
# 下版本计划
服务治理支持,数据统计(错误率,tp90,tp99等),数据大盘统计,自定义标签(已支持-更新于20181203)等
# 代码下载后如何测试
作者已经将测试类给大家写好,下载源码后clean install(这个应该都会吧,maven仓库作者用的是阿里云的maven私服)

xml方式使用者直接运行上面的1和2即可,先开启服务端再开启客户端,当然如果您在多台机器上开启服务端,那么就自动负载均衡了。
注解方式使用者运行上面的3和4即可,先开启服务端在开启客户端,即可
注意测试的时候需要安装zookeeper服务,如果不想通过zk做服务发现,那么客户端可以进行直连
指定的server列表,逗号分隔,#分隔权重,格式192.168.3.253:6666#10,192.168.3.253:6667#10
详情见参数配置列表,但是这种办法作者是不推荐的,在生产环境下没有心跳和动态上下线功能。
#### 联系作者 :
高级java QQ群:492926917
博客地址:https://www.cnblogs.com/zyl2016/