一不小心实现了RPC
前言
随着最近关注 cim 项目的人越发增多,导致提的问题以及 Bug 也在增加,在修复问题的过程中难免代码洁癖又上来了。
看着一两年前写的东西总是怀疑这真的是出自自己手里嘛?有些地方实在忍不住了便开始了漫漫重构之路。
前后对比
在开始之前先简单介绍一下 cim
这个项目,下面是它的架构图:
简单来说就是一个 IM 即时通讯系统,主要有以下部分组成:
IM-server
自然就是服务端了,用于和客户端保持长连接。IM-client
客户端,可以简单认为是类似于的 QQ 这样的客户端工具;当然功能肯定没那么丰富,只提供了一些简单消息发送、接收的功能。Route
路由服务,主要用于客户端鉴权、消息的转发等;提供一些 http 接口,可以用于查看系统状态、在线人数等功能。
当然服务端、路由都可以水平扩展。
这是一个消息发送的流程图,假设现在部署了两个服务端 A、B 和一个路由服务;其中 ClientA
和 ClientB
分别和服务端 A、B 保持了长连接。
当 ClientA
向 ClientB
发送一个 hello world
时,整个的消息流转如图所示:
- 先通过
http
将消息发送到Route
服务。 - 路由服务得知
ClientB
是连接在ServerB
上;于是再通过http
将消息发送给ServerB
。 - 最终
ServerB
将消息通过与ClientB
的长连接通道push
下去,至此消息发送成功。
这里我截取了 ClientA
向 Route
发起请求的代码: 可以看到这就是利用
okhttp
发起了一个 http
请求,这样虽然能实现功能,但其实并不优雅。
举个例子:假设我们需要对接支付宝的接口,这里发送一个 http 请求自然是没问题;但对于支付宝内部各部门直接互相调用接口时那就不应该再使用原始的 http 请求了。
应该是由服务提供方提供一个 api
包,服务消费者只需要依赖这个包就可以实现接口调用。
当然最终使用的是 http、还是自定义私有协议都可以。
也类似于我们在使用 Dubbo
或者是 SpringCloud
时,通常是直接依赖一个 api
包,便可以像调用一个本地方法一样调用远程服务了,并且完全屏蔽了底层细节,不管是使用的 http 还是 其他私有协议都没关系,对于调用者来说完全不关心。
这么一说是不是有内味了,这不就是 RPC 的官方解释嘛。
对应到这里也是同样的道理,Client
、Route
、Server
本质上都是一个系统,他们互相的接口调用也应当是走 RPC
才合理。
所以我重构之后的变成这样了:
是不是代码也简洁了许多,就和调用本地方法一样了,而且这样也有几个好处:
- 完全屏蔽了底层细节,可以更好的实现业务及维护代码。
- 即便是服务提供方修改了参数,在编译期间就能很快发现,而像之前那样调用是完全不知情的,所以也增加了风险。
绕不开的动态代理
下面来聊聊具体是如何实现的。
其实在上文《动态代理的实际应用》 中也有讲到,原理是类似的。
要想做到对调用者无感知,就得创建一个接口的代理对象;在这个代理对象中实现编码、调用、解码的过程。
对应到此处其实就是创建一个 routeApi
的代理对象,关键就是这段代码:
RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance();
完整源码如下:
其中的 getInstance()
函数就是返回了需要被代理的接口对象;而其中的 ProxyInvocation
则是一个实现了 InvocationHandler
接口的类,这套代码就是利用 JDK
实现动态代理的三板斧。
查看 ProxyInvocation
的源码会发现当我们调用被代理接口的任意一个方法时,都会执行这里的 invoke()
方法。
而 invoke()
方法自然就实现了上图中提到的:编码、远程调用、解码的过程;相信大家很容易看明白,由于不是本次探讨的重点就不过多介绍了。
总结
其实理解这些就也就很容易看懂 Dubbo
这类 RPC
框架的核心源码了,总体的思路也是类似的,只不过使用的私有协议,所以在编解码时会有所不同。
所以大家要是想自己动手实现一个 RPC
框架,不妨参考这个思路试试,当用自己写的代码跑通一个 RPC
的 helloworld
时的感觉是和自己整合了一个 Dubbo
、SpringCloud
这样的第三方框架的感觉是完全不同的。
本文的所有源码:
https://github.com/crossoverJie/cim
你的点赞与分享是对我最大的支持

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
挑战全网最幽默的Vuex系列教程:第六讲 Vuex的管理员Module(实战篇)
写在前面 这一讲是 Vuex 基础篇的最后一讲,也是最为复杂的一讲。如果按照官方来的话,对于新手可能有点难以接受,所以想了下,决定干脆多花点时间,用一个简单的例子来讲解,顺便也复习一下之前的知识点。 首先还是得先了解下 Module 的背景。我们知道,Vuex 使用的是单一状态树,应用的所有状态会集中到一个对象中。如果项目比较大,那么相应的状态数据肯定就会更多,这样的话,store 对象就会变得相当的臃肿,非常难管理。 这就好比一家公司只有老板一个人来管理一样。如果小公司倒还好,公司要是稍微大一点,那就麻烦了。这个时候,老板就会成立各大部门,并给各大部门安排一个主管,把管理的任务分派下去,然后有什么事情需要处理的话,只需要跟这几个主管沟通,由主管再把任务分配下去就行了,这就大大提高了工作效率,也减轻了老板的负担。 那么同样的道理,Module 其实就承担了部门管理员的角色,而 store 就是老板。理解了这一层,那么后面就好办多了,接下来,咱们就一步一步动起手来开始实践。 一、准备工作 这里我们使用官方提供的 vue-cli 来建一个项目「vuex-test」。当然,先得安装 vue-...
-
下一篇
海量数据分库分表方案(一)算法方案
本文主要描述分库分表的算法方案、按什么规则划分。循序渐进比较目前出现的几种规则方式,最后第五种增量迁移方案是我设想和推荐的方式。后续章再讲述技术选型和分库分表后带来的问题。 背景 随着业务量递增,数据量递增,一个表将会存下大量数据,在一个表有一千万行数据时,通过sql优化、提升机器性能还能承受。为了未来长远角度应在一定程度时进行分库分表,如出现数据库性能瓶颈、增加字段时需要耗时比较长的时间的情况下。解决独立节点承受所有数据的压力,分布多个节点,提供容错性,不必一个挂整个系统不能访问。 目的 本文讲述的分库分表的方案,是基于水平分割的情况下,选择不同的规则,比较规则的优缺点。 一般网上就前三种,正常一点的会说第四种,但不是很完美,前面几种迁移数据都会很大影响,推荐我认为比较好的方案五。 方案一:对Key取模,除数逐步递增 方案二:按时间划分 方案三:按数值范围 方案四:一致性Hash理念——平均分布方案(大众点评用这种,200G并且一步到位) 方案五:一致性Hash理念——按迭代增加节点(为了方便增量迁移) 方案六:一致性Hash理念——按范围分库(迭代迁移) 方案选择 方案一:对Key...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- 2048小游戏-低调大师作品
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- MySQL数据库在高并发下的优化方案
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2配置默认Tomcat设置,开启更多高级功能