前言

随着最近关注 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. CSS基础总结 1

    一.什么是 CSS? CSS 指层叠样式表 (Cascading Style Sheets),是一种用来表现 HTML 文档样式的语言,样式定义如何显示 HTML 元素,是能够真正做到网页表现与结构分 ...

  2. [noip模拟]B<构造>

    [题目描述] 在两个n*m的网格上染色,每个网格中被染色的格子必须是一个四联通块(没有任何格子被染色也可以),四联通块是指所有染了色的格子可以通过网格的边联通,现在给出哪些格子在两个网格上都被染色了, ...

  3. Java 异常处理与输入输出

    一.异常 1.1 package exception; import java.util.Scanner; public class ArrayIndex { public static void m ...

  4. 性能优化之三:将Dottrace过程加入持续集成

    之前分享过一篇如何做接口性能分析的文章,但是整个分析过程有点繁琐,需要写一个控制台程序调用被测接口,再预热.启动dottrace追踪,最后才能得到我们想要的性能分析报告.如果有办法一键生成性能分析报告 ...

  5. 实验四 数据查询3-group by等

    实验四 数据库查询 一.  实验内容: 1. Group by语句 2. Having 语句 3. Order by语句 4. Limit语句 5. Union语句 6. Handler语句 二.  ...

  6. 《快速认识 Three.js 》

    此文仅作备份之用,为了更好的阅读体验,建议访问原文链接:<Three.js - 走进3D的奇妙世界.> ,感谢原作者的好文.

  7. Win32程序:与"LPCWSTR"类型的形参不兼容

    出现该问题的原因是通常手动输入的字符串都是LPCSTR类型的, 解决办法如下: 在工程处右键,属性-常规-字符集,将Unicode字符集改为为多字节字符集,应用并确认即可.   字符串常量报错: 在常 ...

  8. 感动,我终于学会了Java对数组求和

    前言 看到题目是不是有点疑问:你确定你没搞错?!数组求和???遍历一遍累加起来不就可以了吗??? 是的,你说的都对,都听你的,但是我说的就是数组求和,并且我也确实是刚刚学会.╮(╯▽╰)╭ 继续看下去 ...

  9. Android如何快速打出100个渠道apk

    测试1分钟900多个包 关键思路就是读文件,如图: Python快速打包脚本: #!/usr/bin/env python import zipfile prefix = 'channel_' cha ...

  10. 2017蓝桥杯日期问题(C++B组)

    标题:日期问题小明正在整理一批历史文献.这些历史文献中出现了很多日期.小明知道这些日期都在1960年1月1日至2059年12月31日.令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的, ...