深入理解rpc框架(二):实现“屌丝版”rpc

开始之前我还是先抛出几个问题:

如何做到HA?

如何针对并发场景?

能不能做到服务超时处理、失败重试以及确定不可用之后的熔断保护?

如何做到将请求均匀分发(路由)到rpc服务上?

对于以上问题,我们先不做解答,画了一张图来帮助大家理解:

图中的大概思路是:

客户端消费时,先从zk注册中心拿到可用的服务列表

接口动态代理类将请求信息(接口名,方法名,方法参数类型,方法参数,服务器地址)使用Netty客户端连接Netty服务端并发送

Netty服务端解析请求并调用响应的Handler处理请求并相应

使用Netty代替之前的jdk自带bio来提高性能

接下来我们就一步一步按照上述图中的描述来实现一款新的rpc框架。

①新建如下图的项目结构

rpc是聚合项目,提供版本管理通用依赖管理;rpc-common提供通用变量和一些工具类的管理;rpc-registry是一个注册中心接口声明;rpc-registry-zk提供zk版本的默认注册中心实现;rpc-server提供rpc服务的启动与服务信息注册以及服务调用的处理。

注:项目依赖spring,netty,zk等

项目之间的依赖关系图如下:

②编写rpc-common中的工具

RpcEncoder继承Netty提供的MessageToByteEncoder并重写encode方法:

RpcDecoder继承Netty提供的ByteToMessageDecoder,重写解码decode方法:

RpcRequest是rpc服务调用请求封装类:

RpcResponse是rpc服务调用后的响应:

工具类SerializationUtil,提供了两个方法,将对象序列化成字节数组和将字节数组反序列化成对象(使用Protostuff序列化工具,性能比较好):

③编写注册中心rpc-registry及其实现rpc-registry-zk(也可以其他实现):

ServiceDiscovery定义根据接口发现服务的功能:

ServiceRegistry定义将服务注册到注册中心的功能:

ZkServiceDiscovery从zk中查找并返回可用的服务对应的address:

ZkServiceRegistry提供将服务注册到zk的功能:

多个服务注册后zk中的节点tree结构如下:

每个节点存储的数据是类似127.0.0.1:8000

④编写rpc服务端rpc-server:

@RpcService注解标注一个服务

RpcServer是服务端最核心的一个类,通过spring扩展接口的特性自动启动服务器(Netty):

RpcServerHandler Netty服务器接收到消息之后具体的处理器:

⑤编写rpc客户端rpc-client:

RpcProxy动态代理类:

RpcClient是与rpc服务器(Netty)打交道的客户端:

现在rpc框架已经大致写好了,那接下来就是应用与测试了,我们创建三个项目rpc-sample-api服务接口定义,rpc-sample-server服务实现及提供者,rpc-sample-client服务消费者:

①rpc-sample-api中定义一个简单接口:

②rpc-sample-server中定义①中的接口实现以及程序启动功能:

log4j.properties是日志相关属性定义,rpc.properties中定义zk地址和rpc服务绑定地址端口

spring.xml中定义注册中心bean,以及rpc服务器bean

当然这所有的配置需要有一个门面类来启动(RpcBootstrap):

③rpc-sample-client充当消费者并编写测试逻辑:

log4j.properties以及rpc.properties和②中相似,spring.xml中定义服务发现bean和动态代理工具类bean

服务消费测试类Consumer

④这样服务端和消费端的逻辑都编写好了,接下来启动服务端和消费端测试。

先运行rpc-sample-server中的RpcBootstrap,再运行rpc-sample-client中的Consumer:

看到如上结果,可以认定服务启动成功,然后运行Consumer

可以看到服务已经调用成功并且返回正确结果,到此我们的“屌丝版”rpc已经编写完成和测试通过。

经过改进后的rpc框架虽然变得有模有样,但是仔细的看官仍会发现其存在很多不足,总结出来大概有以下几点:

服务调用失败没有重试机制

没有熔断保护机制

没有很优秀的路由机制

服务调用链不健壮:如果zk失去连接(宕机),消费端没有缓存机制来保持rpc服务端地址,当然服务端也无法继续注册服务

注册中心比较单薄,并且消费端和服务端对zk过度依赖,这个和④优点类似,注册中心没有集群做HA

消费端调用接口服务生成的动态代理类是手动创建,没有和spring融合实现@Autowired自动注入

这一篇利用Netty和spring实现了一款简单的rpc框架,相信大家对rpc的原理有了更深层次的认识和理解,接下来我们会对市面上比较流行的开源rpc框架针对其优缺点做分析。

代码地址:https://gitee.com/ScorpioAeolus/myRpc

创作不易,请多多支持!

附带公众号:

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180318G1AVVN00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券