技术栈
协议序列化/反序列化
网络通信基于 TCP/IP 为基础自定义应用层协议,常见的序列化/反序列化工具有
java 原生序列化、json 、kryo 、protobuf 、fst 和 hessian 等。
在不考虑跨语言的情况下,从序列化时长/序列化大小/易用性/扩展性这几方面考虑,综合性比较强的是 kryo ,但不支持跨语言,protobuf 性能最强且支持跨语言,但是使用时需要事先基于 proto 生成一个类。
最终选择 kryo 和 protobuf 两种序列化工具,使用的时候可选序列化类型,前者序列化几乎不受限制,后者支持跨语言,但是必须事先生成 proto 类型的类并使用其作为序列化工具。
通信框架使用
高性能异步非阻塞框架非 Netty 不可了,客户端和服务端基于 Netty 开发可事半功倍。
除了基于 netty 外,有时需要更小的包依赖,所以 client 除了支持基于 netty 模块,还会开发一个无任何依赖的模块 mini-client ,打完包仅几十 kb 。
服务注册和发现
注册中心选择 zookeeper 作为服务注册和服务发现,当然如果只用单点模式的话其实是不需要注册中心的,所以 zookeeper 是可选组件。
快速使用
1.定义实体作为序列化的对象(可选)
@ShadowEntity
public class MyMessage {
@ShadowField(1)
private String content;
@ShadowField(2)
private int num;
}
如果是 protobuf 序列化方式,定义 proto 格式再用 maven 插件 protobuf-maven-plugin 生成实体
message MyMessage {
string content = 1;
int32 num = 2;
}
2.编写接口和服务类
@ShadowInterface
public interface IHello {
String hello(String msg);
MyMessage say(MyMessage message);
}
然后编写服务实现类
@ShadowService(serviceName = "helloservice")
public class HelloService implements IHello {
@Override
public String hello(String msg) {
return "Hello,"+msg;
}
@Override
public MyMessage say(MyMessage message) {
MyMessage message1 = new MyMessage();
message1.setContent("hello received "+"("+message.getContent()+")");
message1.setNum(message.getNum()+1);
return message1;
}
}
3.最后指定序列化类型和端口,启动服务端
单点启动模式如下:
ServerBuilder.newBuilder()
.serverConfig(serverConfig)
.addPackage("rpctest.hello")
.build()
.start();
使用 zk 作为注册中心集群模式启动
String ZK_URL = "localhost:2181";
ServerConfig serverConfig = new ServerConfig();
serverConfig.setGroup("DefaultGroup");
serverConfig.setPort(2023);
serverConfig.setRegistryUrl(ZK_URL);
serverConfig.setQpsStat(true); //统计 qps
serverConfig.setSerializer(SerializerEnum.KRYO.name());
ServerBuilder.newBuilder()
.serverConfig(serverConfig)
.addPackage("rpctest.hello")
.build()
.start();
4.客户端调用 rpc 服务
ModulePool.getModule(ClientModule.class).init(new ClientConfig());
ShadowClient shadowClient = new ShadowClient("127.0.0.1",2023);
shadowClient.init();
IHello helloService = shadowClient.createRemoteProxy(IHello.class,"shadowrpc://DefaultGroup/helloservice";
MyMessage message = new MyMessage();
message.setNum(100);
message.setContent("Hello, Server!");
System.out.printf("发送请求 : %s\n",message);
MyMessage response = helloService.say(message);
System.out.printf("接收服务端消息 : %s\n",response);
使用 zk 作为服务发现负载均衡调用各个服务器
ClientConfig config = new ClientConfig();
config.setSerializer(SerializerStrategy.KRYO.name());
ModulePool.getModule(ClientModule.class).init(config);
String ZK_URL="localhost:2181";
ShadowClientGroup shadowClientGroup = new ShadowClientGroup(ZK_URL);
shadowClientGroup.init();
IHello helloService = shadowClientGroup.createRemoteProxy(IHello.class, "shadowrpc://DefaultGroup/helloservice");
List shadowClientList = shadowClientGroup.getShadowClients("DefaultGroup");
System.out.println("所有服务器: "+shadowClientList.stream().map(c-> c.getRemoteIp()+":"+c.getRemotePort()).collect(Collectors.toList()));
for(int i = 0 ;i
源码
篇幅有限,所有源码见: https://github.com/Liubsyy/ShadowRPC
目前仅供学习交流使用,后续我将逐步打磨此 rpc 框架达到企业级水准。