拓展|如何设计一个短小精悍、可拓展的RPC框架?(含实现代码)( 二 )
注册中心设计
这里分为几个步骤,首先保存了接口配置,接着初始化注册中心,因为注册中心可能会提供多种来供用户选择,所以这里需要定义一个注册中心的接口:
这里我们提供一个注册的方法,这个方法的语义是将clazz对应的接口注册到注册中心。接收两个参数,一个是接口的class对象,另一个是注册信息,
里面包含了本机的一些基本信息,如下:
好了,定义好注册中心,回到之前的实例化注册中心的地方,代码如下:
这里逻辑也非常简单,就是根据url的schema来判断是那个注册中心
注册中心这里实现了2个实现类,分别使用zookeeper作为注册中心,另外一个是使用广播的方式作为注册中心。
广播注册中心这里仅仅是做个示范,内部没有实现。我们主要是实现了zookeeper的注册中心。
(当然了,如果有兴趣,可以实现更多的注册中心供用户选择,比如redis之类的,这里只是为了保持“拓展点”)
那么实例化完注册中心之后,回到上面的代码:
注册服务提供者
这里逻辑很简单,就是获取本机的的基本信息构造成RegistryInfo,然后调用了doRegistry方法:
这里做了2件事情:
将接口注册到注册中心中
对于每一个接口的每一个方法,生成一个唯一标识,保存在interfaceMethods集合中
下面分别分析这两件事情,首先是注册方法:
因为我们用到了zookeeper,为了方便,引入了zookeeper的客户端框架curator
接着看代码:
zookeeper注册中心在初始化的时候,会建立好连接。然后注册的时候,针对clazz接口的每一个方法,都会生成一个唯一标识
这里使用了InvokeUtils.buildInterfaceMethodIdentify方法:
其实就是对接口的方法使用他们的限定名和参数来组成一个唯一的标识,比如 生成的大概是这样的:
接下来的逻辑就简单了,在Zookeeper中的/myRPC路径下面建立临时节点,节点名称为我们上面的接口方法唯一标识,数据内容为机器信息。
之所以采用临时节点是因为:如果机器宕机了,连接断开之后,消费者可以通过zookeeper的watcher机制感知到
大概看起来是这样的:
通过这样的方式,在服务消费的时候就可以拿到这样的注册信息,然后知道可以调用那台机器的那个端口。
好了,注册中心弄完了之后,我们回到前面说的注册方法做的第二件事情,我们将每一个接口方法标识的方法放入了一个map中:
这个的原因是因为,我们在收到网络请求的时候,需要调用反射的方式调用method对象,所以存起来。
启动网络服务端接受请求
接下来我们就可以看第四步了:
因为这里使用Netty来做的所以需要引入Netty的依赖:
接着来分析:
这部分主要的都是netty的api,我们不做过多的说明,就简单的说一下:
我们通过“&&”作为标识符号来区分两条信息,然后一条信息的最大长度为1MB
所有逻辑都在RpcInvokeHandler中,这里面传进去了配置的服务接口实例,以及服务接口实例每个接口方法唯一标识对应的Method对象的Map集合。
这里说明一下上面的逻辑:
channelRead方法用于接收消息,接收到的就是我们前面分析的那个JSON格式的数据,接着我们将消息解析成RpcRequest
接着从request中解析出来需要调用的接口,然后通过反射调用对应的接口,得到结果后我们将响应封装成PrcResponse写回给客户端:
里面包含了请求的结果JSON串,接口方法唯一标识,请求ID。数据大概看起来这个样子:
通过这样的信息,客户端就可以通过响应结果解析出来。
测试服务提供者
既然我们代码写完了,现在需要测试一把:
首先我们先写一个HelloService的实现类出来:
接着编写服务提供者代码:
接着启动起来,看到日志:
这个时候,我们期望用NettyClient发送请求:
得到的响应应该是:
那么,可以编写一个测试程序(这个程序仅仅用于中间测试用,读者不必理解):
启动之后,看到控制台输出:
bingo,完美实现了RPC的服务提供者。接下来我们只需要实现服务消费者就完成了。
开发服务消费者
服务消费者是同样的处理,我们同样要定义一个消费者的配置:
然后我们是统一入口,在ApplicationContext中修改代码:
在注册的时候,我们需要将需要消费的接口,通过注册中心抓取出来,所以注册中心要增加一个接口方法:
获取服务提供者的机器列表
具体在Zookeeper中的实现如下:
其实就是去zookeeper获取节点中的数据,得到接口所在的机器信息,获取到的注册信息诸侯,就会调用以下代码:
我们会针对每一个唯一的RegistryInfo建立一个连接,然后有这样一段代码:
设置一个callback,用于收到消息的时候,回调这里的代码,这部分我们后面再分析。
然后在client.getCtx()的时候,同步阻塞直到连接完成,建立好连接后通过,NettyClient的代码如下:
这里主要是用了wait()和notifyAll()来实现同步阻塞等待连接建立。
建立好连接后,我们保存到集合中:
发送请求
好了,到了这里我们为每一个需要消费的接口建立了网络连接,接下来要做的事情就是提供一个接口给用户获取服务提供者实例:
推荐阅读
- 流感|流感如何应对?听听郑州市九院儿科主任杨平怎么说的
- 蠢萌新闻|《急先锋》成龙的“接棒”杨洋,年轻演员们表现如何?
- 培训|大湾区青年创意设计 人才培训计划开班
- 观览天下|一块石头卖到十万,看村民们如何发家致富,你心动了吗?
- 乡村波比|野猪被禁止食用,农村野猪泛滥,糟蹋庄稼,该如何解决?
- 气象卫星|一文读懂风云气象卫星50年如何改变中国,未来两次发射“值得期待”
- 科学|世界3大未解之谜:生命起源、地球内核、宇宙之谜,该如何解释?
- 光速|如何超越光速?|No.227
- 崇明|如何培养“小生态人”?揭开崇明推进生态教育的“学校密码”
- 颜夕趣史|?40年前,那个站在长城喝可乐的小男孩,他后来如何了?
