前言

随着最近关注 cim 项目的人越发增多,导致提的问题以及 Bug 也在增加,在修复问题的过程中难免代码洁癖又上来了。

看着一两年前写的东西总是怀疑这真的是出自自己手里嘛?有些地方实在忍不住了便开始了漫漫重构之路。

前后对比

在开始之前先简单介绍一下 cim 这个项目,下面是它的架构图:

简单来说就是一个 IM 即时通讯系统,主要有以下部分组成:

  • IM-server 自然就是服务端了,用于和客户端保持长连接。
  • IM-client 客户端,可以简单认为是类似于的 QQ 这样的客户端工具;当然功能肯定没那么丰富,只提供了一些简单消息发送、接收的功能。
  • Route 路由服务,主要用于客户端鉴权、消息的转发等;提供一些 http 接口,可以用于查看系统状态、在线人数等功能。

当然服务端、路由都可以水平扩展。


这是一个消息发送的流程图,假设现在部署了两个服务端 A、B 和一个路由服务;其中 ClientAClientB 分别和服务端 A、B 保持了长连接。

ClientAClientB 发送一个 hello world 时,整个的消息流转如图所示:

  1. 先通过 http 将消息发送到 Route 服务。
  2. 路由服务得知 ClientB 是连接在 ServerB 上;于是再通过 http 将消息发送给 ServerB
  3. 最终 ServerB 将消息通过与 ClientB 的长连接通道 push 下去,至此消息发送成功。

这里我截取了 ClientARoute 发起请求的代码:



可以看到这就是利用 okhttp 发起了一个 http 请求,这样虽然能实现功能,但其实并不优雅。

举个例子:假设我们需要对接支付宝的接口,这里发送一个 http 请求自然是没问题;但对于支付宝内部各部门直接互相调用接口时那就不应该再使用原始的 http 请求了。

应该是由服务提供方提供一个 api 包,服务消费者只需要依赖这个包就可以实现接口调用。

当然最终使用的是 http、还是自定义私有协议都可以。

也类似于我们在使用 Dubbo 或者是 SpringCloud 时,通常是直接依赖一个 api 包,便可以像调用一个本地方法一样调用远程服务了,并且完全屏蔽了底层细节,不管是使用的 http 还是 其他私有协议都没关系,对于调用者来说完全不关心。

这么一说是不是有内味了,这不就是 RPC 的官方解释嘛。

对应到这里也是同样的道理,ClientRouteServer 本质上都是一个系统,他们互相的接口调用也应当是走 RPC 才合理。

所以我重构之后的变成这样了:

是不是代码也简洁了许多,就和调用本地方法一样了,而且这样也有几个好处:

  • 完全屏蔽了底层细节,可以更好的实现业务及维护代码。
  • 即便是服务提供方修改了参数,在编译期间就能很快发现,而像之前那样调用是完全不知情的,所以也增加了风险。

绕不开的动态代理

下面来聊聊具体是如何实现的。

其实在上文《动态代理的实际应用》 中也有讲到,原理是类似的。

要想做到对调用者无感知,就得创建一个接口的代理对象;在这个代理对象中实现编码、调用、解码的过程。

对应到此处其实就是创建一个 routeApi 的代理对象,关键就是这段代码:

RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance();

完整源码如下:

其中的 getInstance() 函数就是返回了需要被代理的接口对象;而其中的 ProxyInvocation 则是一个实现了 InvocationHandler 接口的类,这套代码就是利用 JDK 实现动态代理的三板斧。

查看 ProxyInvocation 的源码会发现当我们调用被代理接口的任意一个方法时,都会执行这里的 invoke() 方法。

invoke() 方法自然就实现了上图中提到的:编码、远程调用、解码的过程;相信大家很容易看明白,由于不是本次探讨的重点就不过多介绍了。

总结

其实理解这些就也就很容易看懂 Dubbo 这类 RPC 框架的核心源码了,总体的思路也是类似的,只不过使用的私有协议,所以在编解码时会有所不同。

所以大家要是想自己动手实现一个 RPC 框架,不妨参考这个思路试试,当用自己写的代码跑通一个 RPChelloworld 时的感觉是和自己整合了一个 DubboSpringCloud 这样的第三方框架的感觉是完全不同的。

本文的所有源码:

https://github.com/crossoverJie/cim

你的点赞与分享是对我最大的支持

一不小心实现了RPC的更多相关文章

  1. RabbitMQ 实现RPC

    实现RPC 首先要弄明白,RPC是个什么东西. (RPC) Remote Procedure Call Protocol 远程过程调用协议 在一个大型的公司,系统由大大小小的服务构成,不同的团队维护不 ...

  2. 基于RabbitMQ的Rpc框架

    参考文档:https://www.cnblogs.com/ericli-ericli/p/5917018.html 参考文档:RabbitMQ 实现RPC MQ的使用场景大概包括解耦,提高峰值处理能力 ...

  3. 从RPC开始(一)

    这是一篇关于纯C++RPC框架的文章.所以,我们先看看,我们有什么? 1.一个什么都能干的C++.(前提是,你什么都干了) 2.原始的Socket接口,还是C API.还得自己去二次封装... 3.C ...

  4. RPC 使用中的一些注意点

    最近线上碰到一点小问题,分析其原因发现是出在对 RPC 使用上的一些细节掌握不够清晰导致.很多时候我们做业务开发会把 RPC 当作黑盒机制来使用,但若不对黑盒的工作原理有个基本掌握,也容易犯一些误用的 ...

  5. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  6. 游戏编程系列[1]--游戏编程中RPC协议的使用[3]--体验

    运行环境,客户端一般编译为.Net 3.5 Unity兼容,服务端因为用了一些库,所以一般为4.0 或往上.同一份代码,建立拥有2个项目.客户端引用: WindNet.Client服务端引用: OpL ...

  7. python通过protobuf实现rpc

    由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行g ...

  8. spider RPC入门指南

    本部分将介绍使用spider RPC开发分布式应用的客户端和服务端. spider RPC中间件基于J2SE 8开发,因此需要确保服务器上安装了JDK 8及以上版本,不依赖于任何额外需要独立安装和配置 ...

  9. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

随机推荐

  1. 潘粤明的《龙岭迷窟》到底怎么样?我用 Python 得出了一些结论!

    对于天下霸唱的鬼吹灯,相信很多小伙伴都知道,它可谓是国内盗墓寻宝系列小说的巅峰之作,最近得知该系列小说的<龙岭迷窟>部分被制作成了网剧,已经于 4 月 1 日开播了,主要演员潘粤明.姜超. ...

  2. VRRP协议:Virtual Route

    VRRP协议:Virtual Route  Redundancy Protocol虚拟路由冗余协议.是一种容错协议,保证当主机的下一跳路由出现故障时,由另一台路由器来代替出现故障的路由器进行工作,从而 ...

  3. 曹工说Redis源码(1)-- redis debug环境搭建,使用clion,达到和调试java一样的效果

    概要 最近写了spring系列,这个系列还在进行中,然后有些同学开始叫我大神,然后以为我各方面都比较厉害,当然了,我是有自知之明的,大佬大神什么的,当作一个称呼就好,如果真的以为自己就是大神,那可能就 ...

  4. 使用vue-cli脚手架创建vue项目

    使用vue-cli脚手架创建vue项目 首先,你已经安装了node. 使用vue-cli@2 创建项目 执行 命令: npm i -g vue-cli@2 //全局安装vue-cli@2脚手架 : v ...

  5. Java 中的递归

    递归 递归 一种通过调用某个方法来描述需要重复进行的操作.该方法的特点就是可以自己调用自己. 案例一 排队的问题 在生活中,我们经常需要排队.在排队中,我们怎么才能知道自己所排在第几位呢? 我们也许会 ...

  6. Three.js中的动画实现02-[Three.js]-[Object3D属性.onAfterRender/.onBeforeRender]

    Table Of Content Object3D简介以及两个属性的介绍 一个示例 Object3D简介以及两个属性的介绍 这是Three.js中大部分对象的基类,提供了一系列的属性和方法来对三维空间 ...

  7. 查看手机wifi密码

    方法一 手机共享wifi,获得二维码,之后解码获得密码. 二维码解吗工具: https://jiema.wwei.cn/ 方法二 手机扫描wifi共享的二维码后,会有提示信息,其中会显示出密码.

  8. vue中的js引入图片,使用require相关问题

    vue中的js引入图片,必须require进来 或者引用网络地址 <template> <div class="home"> <img alt=&qu ...

  9. 1031 Hello World for U (20分)

    Given any string of N (≥) characters, you are asked to form the characters into the shape of U. For ...

  10. LVS 集群与存储《路由转发》

                                                             LVS 集群与存储<路由转发> 集群简介 u 什么是集群 •  一组通过高 ...