随着分布式架构运用的越来越多,RPC框架成为了我们不得不掌握的知识,这里一步一步来手写一个简单的RPC框架,以博文作为记录及自我监督。
首先是技术选型,这边我选用的是当前比较流行的Netty+Zookeeper来实现,通过zookeeper的特性来实现服务注册与发现,通信则使用netty框架。
这里贴出github代码地址,想直接看代码的可以直接下载运行:https://github.com/whiteBX/wrpc
这里先来讲服务注册发现原理: 利用zookeeper的创建临时节点和watcher机制,可以做到在一个服务下注册多个服务器地址,并且在节点发生变动时通过watcher动态更新服务器列表,来达到在新增/修改/删除时自动注册发现/删除/更新服务器连接信息.这里说一点,zookeeper的增删改操作会交由leader去处理,所以这里不用担心并发问题.
zookeeper相关代码如下:
* 获取zookeeper连接
*
connectString sessionTimeout
zooKeeper
countDownLatch
zooKeeper connectString sessionTimeout
watchedEvent
watchedEvent
countDownLatch
countDownLatch
e
out e
e
out e
out
zooKeeper
* 创建临时节点
*
zk appCode data
zk appCode
path zk ZK_REGISTORY_ROOT_PATH appCode data
OPEN_ACL_UNSAFE EPHEMERAL_SEQUENTIAL
out path
e
out e
* 初始化appPath
*
zk appCode
zk
zk ZK_REGISTORY_ROOT_PATH appCode
zk ZK_REGISTORY_ROOT_PATH appCode OPEN_ACL_UNSAFE
PERSISTENT
e
out e
* 初始化根节点
*
zk
zkZK_REGISTORY_ROOT_PATH
zkZK_REGISTORY_ROOT_PATH OPEN_ACL_UNSAFE
PERSISTENT
e
out e
接下来看provider相关类,提供服务:
* 1.创建netty监听,等待客户端连接 *
* netty客户端
nettyClient
* zookeeper客户端
zkClient
server port
nettyClientport
zooKeeper zkClientZK_CONNECTION_STRING
ZK_SESSION_TIME_OUT
serverIp server COMMOA port
zkClientzooKeeper APP_CODE serverIp
代码中注释已经很完整了,文中用到的几个常量自己定就行,appCode作为服务的唯一标识,接下来看代码中用到的NettyClient代码
* 开启新的netty连接 *
port
bootstrap
bootstrap
socketChannel
pipeline socketChannel
pipeline
pipeline
pipeline
bootstrapport
e
out e
这里来看下RpcServerNettyHandler类,他继承自ChannelInboundHandlerAdapter,负责通信数据。
ctx msg
out msg
ctx
到这里provider相关代码就完了,接下来看consumer相关代码:
* url处理器
urlHolder
* netty客户端
nettyClient
* 远程调用
*
appCode param
serverIp urlHolderappCode
serverIp
out
clientHandler
clientHandlerparam
nettyClientserverIp clientHandler
result clientHandler
out serverIp param result
result
e
out e
这里来看一下UrlHolder类,它负责连接zookeeper,并获取其中服务地址列表,随机返回其中一条:
* url列表
urlList
* zk客户端
zkClient
* 获取URL
appCode
urlList
appCode
urlList
urlList urlList
out
* 初始化urlList
appCode
zk zkClientZK_CONNECTION_STRING
ZK_SESSION_TIME_OUT
urlNodeList zk
ZK_REGISTORY_ROOT_PATH appCode
watchedEvent
appCode
urlNodeList
urlList
path urlNodeList
url zkpath
watchedEvent
appCode
url
urlList url
urlList urlList
e
out e
* url列表
urlList
* zk客户端
zkClient
* 获取URL
*
appCode
urlList
appCode
urlList
urlList urlList
out
* 初始化urlList
*
appCode
zk zkClientZK_CONNECTION_STRING
ZK_SESSION_TIME_OUT
appPath ZK_REGISTORY_ROOT_PATH appCode
urlNodeList zkappPath
watchedEvent
appCode
urlNodeList
urlList
path urlNodeList
url zkappPath path
watchedEvent
appCode
url
urlList url
urlList urlList
e
out e
接下来是netty的initClient方法:
* 开始一个新的客户端连接 *
server clientHandler
urlArray serverCOMMOA
ip urlArray
port urlArray
bootstrap
bootstrap
socketChannel
pipeline socketChannel
pipeline
pipeline
pipelineclientHandler
bootstrapip port
e
out e
然后是这里我们自己主要写的一个通信处理类RpcClientNettyHandler,这个类负责远程调用和处理返回的消息:
context
contextCountDownLatch
countDownLatch
* 入参
param
* 响应
response
* 连上时触发
ctx
context ctx
contextCountDownLatch
* 获取服务器返回信息
ctx msg
response msg
countDownLatch
* 远程调用并返回结果
contextCountDownLatch
contextparam
countDownLatch
response
param
param param
到这里代码实现就写完了,下面是测试:
args
provider
provider
provider
provider
provider
provider
MAX_VALUE
args
consumer
i
consumerAPP_CODE i
日志输出如下:
zookeeper连接成功
临时节点创建成功:registry
zookeeper连接成功
临时节点创建成功:registry
zookeeper连接成功
临时节点创建成功:registry
zookeeper连接成功
临时节点创建成功:registry
zookeeper连接成功
临时节点创建成功:registry
服务端收到请求aaa0
服务端收到请求aaa1
服务端收到请求aaa2
服务端收到请求aaa3
zookeeper连接成功
调用服务器请求参数aaa0响应参数success
调用服务器请求参数aaa1响应参数success
调用服务器请求参数aaa2响应参数success
调用服务器请求参数aaa3响应参数success
调用服务器请求参数aaa4响应参数success
调用服务器请求参数aaa5响应参数success
这边可以修改一下服务端里面注册服务的端口,然后再启多个服务,会发现服务启动起来之后,客户端会自动随机分配到新启动的服务请求,并成功返回。再关掉几个端口的服务,会发现不会在请求那几个端口,这就是zookeeper的watcher机制实现的功能。
到这里就实现了RPC框架的服务注册发现+基本通信功能.
这里可能跟大家平时用的RPC框架差别还挺大:我怎么把我自己的Service注册到provider里,然后客户端通过接口来调用呢?.
关于这个问题,下一篇见!
还没有评论,来说两句吧...