RPC原理分析和架构设计要点

参考资料:

1)轻量级分布式 RPC 框架:http://www.importnew.com/20327.html
2)RPC原理及RPC实例分析:http://www.importnew.com/22003.html
一、基础概念
RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样。
RPC 可基于 HTTP 或 TCP 协议,Web Service 就是基于 HTTP 协议的 RPC,它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC。会两方面会直接影响 RPC 的性能,一是传输方式,二是序列化。

但rpc在实际项目中基于http协议的webService使用的更多。
对于远程调用的基于restful的webservice主要有以下几种实现方式
1.org.springframework.web.client.RestTemplate
2.org.apache.commons.httpclient.HttpClient

可以使用这些组件来进行远程调用,就像是在浏览器或者postman等工具上调用一样的效果

二、RPC原理

由于各服务部署在不同机器,服务间的调用免不了网络通信过程,服务消费方每调用一个服务都要写一坨网络通信相关的代码,不仅复杂而且极易出错。

如果有一种方式能让我们像调用本地服务一样调用远程服务,而让调用者对网络通信这些细节透明,那么将大大提高生产力,比如服务消费方在执行helloWorldService.sayHello(“test”)时,实质上调用的是远端的服务。这种方式其实就是RPC(Remote Procedure Call Protocol),在各大互联网公司中被广泛使用,如阿里巴巴的hsf、dubbo(开源)、Facebook的thrift(开源)、Google grpc(开源)、Twitter的finagle(开源)等。

要让网络通信细节对使用者透明,我们需要对通信细节进行封装,我们先看下一个RPC调用的流程涉及到哪些通信细节:

RPC原理分析和架构设计要点

1.服务消费方(client)调用以本地调用方式调用服务;
2.client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
3.client stub找到服务地址,并将消息发送到服务端;
4.server stub收到消息后进行解码;
5.server stub根据解码结果调用本地的服务;
6.本地服务执行并将结果返回给server stub;
7.server stub将返回结果打包成消息并发送至消费方;
8.client stub接收到消息,并进行解码;
9.服务消费方得到最终结果。

RPC的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。

技术选型

1.序列化
什么是序列化?序列化就是将数据结构或对象转换成二进制串的过程,也就是编码的过程。

什么是反序列化?将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

为什么需要序列化?转换为二进制串后才好进行网络传输嘛!

为什么需要反序列化?将二进制转换为对象才好进行后续处理!

Java 提供了默认的序列化方式,但在高并发的情况下,这种方式将会带来一些性能上的瓶颈,于是市面上出现了一系列优秀的序列化框架,
比如:Protobuf、Kryo、Hessian、Jackson 等,它们可以取代 Java 默认的序列化,从而提供更高效的性能。

2.网路传输
为了支持高并发,传统的阻塞式 IO 显然不太合适,因此我们需要异步的 IO,即 NIO。
Java 提供了 NIO 的解决方案,Java 7 也提供了更优秀的 NIO.2 支持,用 Java 实现 NIO 并不是遥不可及的事情,只是需要我们熟悉 NIO 的技术细节。

3.注册中心
如何让别人使用我们的服务呢?有同学说很简单嘛,告诉使用者服务的IP以及端口就可以了啊。确实是这样,这里问题的关键在于是自动告知还是人肉告知。

人肉告知的方式:如果你发现你的服务一台机器不够,要再添加一台,这个时候就要告诉调用者我现在有两个ip了,你们要轮询调用来实现负载均衡;调用者咬咬牙改了,结果某天一台机器挂了,调用者发现服务有一半不可用,他又只能手动修改代码来删除挂掉那台机器的ip。现实生产环境当然不会使用人肉方式。

有没有一种方法能实现自动告知,即机器的增添、剔除对调用方透明,调用者不再需要写死服务提供方地址?当然可以,现如今zookeeper被广泛用于实现服务自动注册与发现功能!

简单来讲,zookeeper可以充当一个服务注册表(Service Registry),让多个服务提供者形成一个集群,让服务消费者通过服务注册表获取具体的服务访问地址(ip+端口)去访问具体的服务提供者

4.代理模式
怎么封装通信细节才能让用户像以本地调用方式调用远程服务呢?对java来说就是使用代理!java代理有两种方式:
jdk 动态代理
字节码生成
尽管字节码生成方式实现的代理更为强大和高效,但代码维护不易,大部分公司实现RPC框架时还是选择动态代理方式。

根据以上技术需求,我们可使用如下技术选型:

Spring:它是最强大的依赖注入框架,也是业界的权威标准。
Netty:它使 NIO 编程更加容易,屏蔽了 Java 底层的 NIO 细节。
Protostuff:它基于 Protobuf 序列化框架,面向 POJO,无需编写 .proto 文件。
ZooKeeper:提供服务注册与发现功能,开发分布式系统的必备选择,同时它也具备天生的集群能力。
jdk动态代理:

2.1 透明化远程服务调用

怎么封装通信细节才能让用户像以本地调用方式调用远程服务呢?对java来说就是使用代理!java代理有两种方式:

jdk 动态代理
字节码生成
尽管字节码生成方式实现的代理更为强大和高效,但代码维护不易,大部分公司实现RPC框架时还是选择动态代理方式。

具体实现请参考:http://www.importnew.com/22003.html

2.2 对消息进行编码和解码
2.2.1 确定消息数据结构
2.2.2 序列化和反序列化
具体实现请参考:http://www.importnew.com/22003.html

2.3 通信

2.4 requestID

3.发布自己的服务
通过zookeeper来实现服务自动注册与发现功能

具体实现请参考:http://www.importnew.com/22003.html

三、执行流程
1.分布式RPC流程图

RPC原理分析和架构设计要点

RPC原理分析和架构设计要点

待续