动态代理与RPC

简介: 动态代理与RPC

前后对比


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



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


  • IM-server 自然就是服务端了,用于和客户端保持长连接。


  • IM-client 客户端,可以简单认为是类似于的 QQ 这样的客户端工具;当然功能肯定没那么丰富,只提供了一些简单消息发送、接收的功能。


  • Route 路由服务,主要用于客户端鉴权、消息的转发等;提供一些 http 接口,可以用于查看系统状态、在线人数等功能。


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



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


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


  1. 先通过 http 将消息发送到 Route 服务。


  1. 路由服务得知 ClientB 是连接在 ServerB 上;于是再通过 http 将消息发送给 ServerB


  1. 最终 ServerB 将消息通过与 ClientB 的长连接通道 push 下去,至此消息发送成功。


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



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


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


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


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


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


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


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



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


  • 完全屏蔽了底层细节,可以更好的实现业务及维护代码。


  • 即便是服务提供方修改了参数,在编译期间就能很快发现,而像之前那样调用是完全不知情的,所以也增加了风险。


绕不开的动态代理


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


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


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



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


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


完整源码如下:



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



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


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


总结


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


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


本文的所有源码:


github.com/crossoverJi…


相关文章
|
3月前
|
编解码 负载均衡 监控
RPC远程调用
RPC远程调用
|
6月前
|
Java Spring
CGLIB代理使用与原理详解
CGLIB代理使用与原理详解
88 0
|
Java Spring
手动实现aop使用的动态代理和cglib代理
spring的aop使用的是动态代理和cglib代理,在对象有实现接口的情况下使用动态代理,没有实现接口的情况下使用cglib代理。 cglib代理是继承目标对象来创建代理,所以目标对象不能使用final修饰。
|
Java API Maven
动态代理-RPC实现核心原理
实现过统一拦截吗?如授权认证、性能统计,可以用 Spring AOP,不需要改动原有代码前提下,还能实现非业务逻辑跟业务逻辑的解耦。核心就是动态代理,通过对字节码进行增强,在方法调用时进行拦截,以便于在方法调用前后,增加处理逻辑。
155 0
|
Java Spring 容器
Feign源码分析-接口如何发现并生成代理类
Feign源码分析-接口如何发现并生成代理类
190 0
Feign源码分析-接口如何发现并生成代理类
|
Java 索引 Spring
|
Java 索引
jdk动态代理和cglib动态代理的原理分析(下)
jdk动态代理和cglib动态代理的原理分析(下)
223 0
jdk动态代理和cglib动态代理的原理分析(下)
|
设计模式 Java 索引
jdk动态代理和cglib动态代理的原理分析(上)
jdk动态代理和cglib动态代理的原理分析(上)
jdk动态代理和cglib动态代理的原理分析(上)
|
存储 设计模式 Java
Proxy动态代理机制详解
代理模式给某一个(目标)对象提供一个代理对象,并由代理对象持有目标对象的引用,所谓代理,就是一个对象代表另一个对象执行相应的动作程序。
100 0
Proxy动态代理机制详解
|
Java 应用服务中间件
【EJB学习笔记】——远程调用和本地调用
  EJB应用可以发布为远程调用和本地调用。   从字面意思来理解,远程调用就是客户端(调用的模块)和服务端(被调用的模块)“不在一起”,“相隔很远”;本地调用就是客户端(调用的模块)和服务端(被调用的模块)“在一起”,“相隔很近”。   实质就是,客户端与服务端的EJB对象不在同一个JVM进程中,就是远程调用;客户端与服务端的EJB对象在同一个JVM进程中,就是本地调用。