1.什么是RPC?
RPC(Remote procedure call)远程过程调用,在分布式计算环境中,远程过程调用就是一个程序需要调用另一个地址空间的程序来完成执行(两个程序可以在同一台机器上的不同进程或者在不同机器上的两个进程,甚至可以在不同网络上的两个进程)。RPC使远程调用就像本地调用一样,隐藏了远程交互的实现细节。
2.RPC的原理
RPC基于客户端-服务端模型,是一种实现了请求响应消息传递系统的协议。一个RPC由客户端发起请求,发送带参数的请求到指定的服务端来执行程序。服务端接收请求,处理请求,将处理结果返回给客户端。客户端接收响应,处理结果,然后程序继续执行。服务端在处理请求时,客户端处理阻塞状态,直到接收到服务端的响应才恢复执行。当然客户端也可以发送异步请求,这样程序就不会处于阻塞状态。
为了让客户端能够访问服务端,就必须要约定通信协议。对于不同语言平台的系统,大部分都是采用IDL(接口描述语言)方式来约定通信协议。对于同一语言平台的系统,可以采用直接暴露接口的方式进行通信。
服务治理型RPC框架 跨语言调用型
RPC技术实现原理
- 动态代理
- 序列化
- 协议
RPC服务端将接口以jar包形式提供对外,客户端引入jar包,在本地直接调用接口,以动态代理方式生成服务端接口的实现类,由动态代理负责与服务端的交互。
客户端动态代理与服务端交互过程中需要对传输的数据进行序列化
客户端与服务端以约定的协议进行通信,协议可以是基于RCP、UDP,或HTTP。
客户端调用服务端的实现过程:
- 客户端引入接口jar包,调用接口;
- 使用动态代理技术为接口生成一个代理对象,接口调用直接由这个代理对象接收;
- 代理对象接收到调用请求后,识别远程方法的IP、端口,将参数序列化,然后使用约定的通信协议将数据发送到远程方法中;
RPC与HTTP的区别?
首先说明下这里的HTTP指的是哪一层面。
客户端应用调用服务端应用的实现方式通常有两种:
- 服务端暴露接口,客户端使用HTTP形式调用;
接口遵循RESTFul规范
- 远程过程调用。
- RPC框架需要实现动态代理、序列化与反序列化、通信、异常处理等工作
- 其中通信又可以基于TCP/UDP/HTTP等协议来实现
所以问题中RPC与HTTP的区别?这里的HTTP指的是第一种实现方式,服务端暴露接口使用HTTP调用。
- HTTP协议处于网络模型的第七层,TCP协议处于第四层;
- HTTP优缺点明显,可读性强,特别是基于RESTFul规范的接口,但是通信过程中会携带大量无用信息,比如HTTP报文头等内容,效率低;
- RPC基于动态代理实现,使用简单,就像调用本地方法一样,通信效率高,尤其是基于TCP协议的,可以自定义序列化方式,效能高;
3.RPC框架:原理、实现、对比(优点、缺点)
gRPC
gRPC,由Google公司开源的一个高性能、跨语言的RPC框架。主要面向移动开发并基于HTTP/2协议标准而设计,基于ProtoBuf序列化协议开发,且支持众多开发语言。gRPC,定义一个服务,指定其可以被远程调用的方法及其参数和返回类型。gRPC默认使用protocol buffers作为IDL(接口描述语言),来描述服务接口和有效载荷消息结构。如果有需要的话,可以使用其他替代方案。
开发流程: 1.使用protobuf定义服务接口,即.proto文件。 2.使用编译器工具从.proto的服务定义中生成gRPC客户端和服务端接口代码。通常gRPC用户可以在服务端实现这些接口,并从客户端调用它们。 3.在服务侧,服务端实现服务接口,运行一个gRPC服务器来处理客户端调用。服务器通过监听指定的端口,来等待客户端连接请求,通常使用Netty来构建,gRPC内置了Netty的支持。gRPC底层架构会解码传入的请求,执行服务方法,编码服务应答。 4.在客户侧,客户端有一个stub实现了服务端同样的方法。客户端可以在本地stub调用这些方法,用合适的protocol buffer消息类型封装这些参数,gRPC来负责发送请求给服务端并返回服务端protocol buffer响应;Request和Response均被封装成HTTP2的stream frame,通过Netty Channel进行交互。
不足之处:gRPC尚未提供连接池、服务自动发现、进程内负责均衡机制等高级特性,需要开发人员额外封装。
Thrift
thrift,由Fackbook公司实现的一种高效的、支持多种编程语言的远程服务调用的框架。使用Thrift自定义的语法作为IDL(接口描述语言),自实现的传输层协议。
开发流程: 1.使用thrift定义服务接口,即.thrift文件。每个方法包含一个方法名、参数列表和返回类型。每个参数包括参数序号、参数类型以及参数名。Thrift是对IDL描述性语言的一种具体是实现。 2.使用Thrift工具编译.thrift文件,就会生成.java文件。该文件包含了再.thrift文件中描述的服务的接口定义,即.Iface接口,以及服务调用的底层通信细节,包括客户端的调用逻辑.Client以及服务端的处理逻辑.Processor,用于构建客户端和服务端的功能。 3.在服务侧,服务端实现.Iface接口并将实现类传递给Thrift服务器。服务器绑定端口,关联服务实现类,启动服务,监听端口。 4.在客户侧,客户端调用.client访问服务端的逻辑实现。
不足之处:thrift不支持双向通行,且也无负责均衡等高级特性实现。
Dubbo
Dubbo,是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架。Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。
- 透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
- 软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
- 服务自动注册与发现,不再需要写死服务提供地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。 Dubbo支持多种协议:Dubbo、Hessian、HTTP、RMI、WebService、Thrift、Memcached、Redis。Dubbo提供多种注册中心:Multicast、Zookeeper、Redis、Simple。Dubbo使用Netty作为其网络通信框架。
开发流程: 1.服务提供者,定义服务接口(该接口需单独打包,在服务提供方和消费方共享) 2.在服务提供方实现接口(对服务消费方隐藏实现),用Spring配置声明暴露服务,加载Spring配置,启动服务。 3.服务消费者,通过Spring配置引用远程服务,加载Spring配置,并调用远程服务。
不足之处:Dubbo目前只支持Java语言平台,不支持跨语言架构。
Motan
Motan,是一套基于Java开发的、高性能、易于使用的分布式远程服务调用RPC框架,除了常规的点对点调用外、Motan还提供服务治理功能,包括服务节点的自动发现、摘除、高可用和负载均衡等,由新浪微博开源。Motan具有良好的扩展性,主要模块都提供了多种不同的实现,例如支持多种注册中心、支持多种RPC协议等。Motan使用hessian2进行消息序列化,使用Netty进行远程通信,使用consul或zookeeper作为其服务注册中心。 功能:
- 支持通过Spring配置方式集成,无需额外编写代码即可为服务提供分布式调用能力。
- 支持集成consul、zookeeper等配置服务组件,提供集群环境的服务发现及治理能力。
- 支持动态自定义负责均衡、跨机房流量调整等高级服务调度能力。
- 基于高并发、高负载场景进行优化,保障生产环境下RPC服务高可用。
架构概述: Motan中分为服务提供方(RPC Server),服务调用方(RPC Client)和服务注册中心(Registry)三个角色。
- Server提供服务,向Registry注册自身服务,并向注册中心定期发送心跳汇报状态。
- Client使用服务,需要向注册中心订阅RPC服务,Client根据Registry返回的服务列表,与具体的Sever建立连接,并进行RPC调用。
- 当Server发生变更时,Registry会同步变更,Client感知后会对本地的服务列表作相应调整。
开发流程: 1.在pom中添加Motan包相关依赖 2.为调用方和服务方创建公共接口 3.编写业务接口逻辑、创建并启动RPC Server 4.创建并执行RCP Client
不足之处:Motan目前只支持Java语言平台,不支持跨语言架构。
4.Motan研究:实现、原理、优缺点
Motan框架采用模块化设计,使用时可以按需依赖。目前的模块有:
- motan-core Motan核心框架
- motan-transport-netty 基于Netty协议的长连接传输协议
- motan-registry-consul Consul服务发现组件
- motan-registry-zookeeper Zookeeper服务发现组件
- motan-springsupport Spring标签解析相关功能
1.定义接口 Motan是基于Java语言的RPC框架,所以只需定义好接口,并将接口单独打包,服务端和客户端分别引用包即可。
2.服务端实现,服务端发布
启动
Motan通过Spring配置方式启动服务端,完成服务的发布和注册。通过Netty启动服务发布,并将服务发布到Zookeeper上。
motan-core模块的config
包内定义了motan自定义支持的配置文件实现类。
发布
com.weibo.api.motan.config.ServiceConfig
的export
方法实现了服务的发布。ServiceConfig
调用默认的配置处理程序SimpleConfigHandler
的export
方法发布和注册服务。SimpleConfigHandler
调用默认的motan协议DefaultRpcProtocol
创建一个服务DefaultRpcExporter
。DefaultRpcExporter
最终调用EndpointFactory
创建了一个NettyServer
服务。自此服务创建完成。
3.客户端调用(客户端什么时候调用服务端?启动的时候还是在调用的时候?)
调用
在进行RPC请求时,客户端通过代理机制调用cluster包,cluster根据配置的HA和LoadBalance选出一个可用的Server,通过serialize模块把RPC请求转换为字节流,然后通过transport模块发送到服务端。
4.序列化协议 消息序列化使用Hessian2序列化协议,Hessian是一个二进制网络服务协议,非常适合用于发送二进制数据。 在motan-core核心框架中定义了Hessian2协议的使用。
- 在com.weibo.api.motan.common.URLParamType枚举类中定义了序列化协议
serialize("serialization", "hessian2")
使用hessinan2
作为序列化协议。 - 在com.weibo.api.motan.serialize包中定义了
Hessian2
的序列化实现类Hessian2Serialization
,分别实现了序列化serialize
和反序列化deserialize
方法。 - 在com.weibo.api.motan.codec包,Codec接口中定义了编解码方法
encode
和decode
,AbstractCodec抽象类实现了Codec
接口,并新增了serialize
和deserialize
方法实现。com.weibo.api.motan.protocol.rpc.DefaultRpcCodec继承了AbstractCodec
,实现了接口Codec
定义的编解码方法。DefaultRpcCodec
在方法实现中指定了序列化实现类为Hessian2Serialization
。DefaultRpcCodec
为Motan协议中RPC的默认的编解码实现类。 - Motan在服务的接入机制支持两种方式,一种是基于Java的SPI机制,另一种是Motan自定义的基于注解的weibo:api配置方式。在motan-core/src/main/resources/META-INF/services包内定义了服务接口命名文件,例如
com.weibo.api.motan.codec.Codec
接口文件,并在文件内定义了两个接口的实现类名com.weibo.api.motan.protocol.rpc.DefaultRpcCodec
和com.weibo.api.motan.protocol.rpc.CompressRpcCodec
。表明Motan的编解码支持两种实现,一种是默认的RPC编解码实现,另一种是支持压缩的RPC编解码实现。 - 在com.weibo.api.motan.core.extension包内实现了服务接入机制,
ExtensionLoader
服务扩展加载类。Motan传输的服务端和客户端的编解码实现类就是通过ExtensionLoader
获取。
ExtensionLoader.getExtensionLoader(Codec.class).getExtension(url.getParameter(URLParamType.codec.getName(), URLParamType.codec.getValue()));
- 在com.weibo.api.motan.transport包内的
AbstractServer
和AbstractClient
抽象服务端和客户端分别都持有Codec
的引用。用于创建服务端和客户端时指定的编解码类。
5.通信协议 (还要考虑服务端接收到请求后,如何处理?)
服务端通信
Motan默认使用Netty nio的TCP长链接方式进行远程通信。
motan-core模块的transport包内定义了远程通信基础类库,服务端抽象类AbstractServer
、客户端抽象类AbstractClient
、终结点工厂接口类EndpointFactory
、消息处理接口类MessageHandler
默认的motan协议,通过weibo:spi方式构建终结点工厂类,通过工厂创建服务端和客户端实例。
motan-transport-netty模块实现了motan的远程通信,定义了终结点工厂实现类NettyEndpointFactory
,并通过spi方式在META-INF.services
文件中定义了com.weibo.api.motan.transport.EndpointFacotry
服务接口名文件,在文件中指定了服务实现类com.weibo.api.motan.transport.netty.NettyEndpointFactory
。NettyEndpointFactory
实现了服务端和客户端的创建工作。
motan-transport-netty模块内的NettyServer
继承实现了motan-core模块transport
包定义的AbstractServer
方法。
客户端通信
客户端启动与服务端类似,通过NettyEndpointFactory
实例化NettyClient
。
服务注册与发现(这个后面在分析,因为motan可以采用直接连接方式来发现服务)
Motan通过Netty启动服务,不过为了能够让客户端发现服务。Motan采用了Zookeeper机制,注册服务。
服务发布
服务发布、订阅、通知
motan-core模块定义接口和抽象类,然后其他模块具体实现接口和继承抽象类。
motan-registry-zookeeper motan-registry-consul
高可用策略(容错策略)
失败重试(Failover)、快速失败(Failfast)、异常隔离(Server 连续失败超过指定次数置为不可用,然后定期进行心跳探测)
快速失败(Failfast),方法调用一次就结束,不管成功与否。
失败重试(Failover),设置重试次数,方法调用失败则重新调用,如果是业务异常导致的失败或者是重试次数已达设置次数则直接结束返回。
负载均衡
支持低并发优先、一致性 Hash、随机请求、轮询等
流量压缩
签名、压缩
客户端发送请求: requestId arguments methodName parameterDesc interfaceName attachment:[ requestIdFromClient version clientGroup application module group ]
服务端接收请求: version group interfaceName (serviceKey: group,interfaceName,version)
methodName parameterDesc
arguments
服务端返回响应: value rpcProtocolVersion(从request中获取) attachements(从request中获取)
requestId(从request中获取) processTime
动态流量调整
客户端订阅服务、订阅命令(group)
RPC简单实现模块组成
- 定义服务接口,编写服务实现类
- 自定义RPC服务注解
- 配置服务端
- 启动并发布服务
- 实现服务注册
- 实现RPC服务器,封装RPC请求,并实现RPC的编码和解码,实现序列化工具类
- 配置客户端
- 实现服务发现
- 实现RPC代理
- 发送RPC请求