RPC要解决的两个问题
- 解决分布式系统中,服务之间的调用问题
- 远程调用时,要能像本地调用一样方便,让调用者感知不到远程调用的逻辑
实现一个RPC框架需要具备的要素
- 暴露服务
- dubbo的服务暴露方式时共享接口
- 远程代理对象,远程服务器的本地代理
- 使用代理模式,结合spring使用。通过spring注入接口对象,注入时,如果扫描到对象加了@Reference注解,就给接口生成一个代理对象,将这个代理对象放入容器中。代理对象内部,通过通信协议来实现远程过程调用(dubbo使用的是基于Http协议的HttpClient)。
- 通过代理,以反射的方式进行调用
- 客户端
- 客户端代理对象调用通讯工具提供的方法,与服务端建立通讯
- 客户端代理对象将信息(要调用的服务名称、要调用的接口名称、要调用的方法名称、请求的参数值等等)放入Request对象中
- 客户端对象将Request对象序列化为二进制字节码
- 客户端调用通讯工具类与服务端建立连接将二进制数据发给服务端
- 客户端通讯工具类接收到服务端的响应后,将响应结果进行反序列化,交给代理对象,代理对象将结果返回给调用远程接口的应用
- 服务端
- 服务使用通讯工具监听端口,接收客户端发来的二进制数据
- 服务通讯工具类端通过反序列化将二进制字节码转为Request对象,并将Request对象交给服务端的代理对象进行处理
- 服务端代理对象从Request对象中获取信息(接口名称、方法名称、参数等)
- 服务端代理对象根据得到的消息,通过反射的方式去调用对应的方法,得到返回值,交给通讯工具类
- 服务端通讯工具类将返回结果序列化为二进制数据,发送给客户端。
- 客户端
- 通信
- 传出协议
- 数据协议
- 序列化,反序列化
- 序列化之后的字节大小和序列化过程的耗时会对性能产生一定的影响
- 序列化技术:json、xml、hessain、Kryo、Avro
- IO方式
优秀的远程框架还需要考虑的功能
- 通用性
- 随时都能根据需求增加新的远程调用接口,并且不用每新增一个接口都增加一个实现类
- 与spring集成
- 结合IOC,简化开发
- 服务端线程池
- 通过线程池,实现同步处理多个RPC请求
- 考虑长连接与短连接
- 不能每次调用RPC接口时都开启一个Socket建立连接。保持若干个长连接,每次由RPC请求时,把请求放到任务队列中,由线程池去消费执行。参考dubbo的实现方式
- 注册中心
- 每个服务可能对应多个服务实例,注册中心可以管理服务实例列表,告诉哥哥服务都存在哪些实例可以调用。实例如何向注册中心注册?又如何从注册中心获取服务实例列表?
- 负载均衡
- 从众多的服务实例中挑一个进行请求,需要通过负载均衡来实现。负载均衡策略有很多,如何实现负载均衡?如何做到负载均衡策略可配置?
- 结果缓存
- 不是每远程调用一次都需要服务端执行相关的方法,一些常用的结果可以缓存起来,直接返回给客户端。如何实现缓存?(Redis等)
- 多版本控制
- 服务端的接口如果修改了,旧的接口怎么办?(适配器模式)
- 异步调用
- 如何实现异步调用
- 稳定停机
- 服务端要停机,队列中还有没处理完的请求怎么办?