先再次重复强调一遍,通信协议不是 rpc 最重要的部分,不要被这类回答带偏。
如果要了解 rpc 请更多的去了解服务治理(SOA)的一些基本策略,推荐去看看 dubbo 的相关文档。
一、详解
-
rpc是远端过程调用,其调用协议通常包含:
传输协议 和 序列化协议
。
- 传输协议:比如著名的 grpc,它底层使用的是 http2 协议;还有 dubbo 一类的自定义报文的 tcp 协议
- 序列化协议:例如基于文本编码的 json 协议;也有二进制编码的 protobuf、hession 等协议;还有针对 java 高性能、高吞吐量的 kryo 和 ftc 等序列化协议 -
因此我理解大部人理解误区的问题应该是:
为什么要使用自定义 tcp 协议的 rpc 做后端进程通信?
-
解答:要解决这个问题就
应该搞清楚 http 使用的 tcp 协议,和我们自定义的 tcp 协议在报文上的区别。
- 首先要
否认
一点 http 协议相较于 自定义tcp 报文协议,增加的开销在于连接的建立与断开。
- 第一、http协议是支持连接池复用的
,也就是建立一定数量的连接不断开,并不会频繁的创建和销毁连接
- 第二、
http也可以使用 protobuf 这种二进制编码协议对内容进行编码
因此二者即 http 和 rpc 最大的区别还是在传输协议上
。
- 通用定义的http1.1协议的tcp报文包含太多废信息,一个POST协议的格式大致如下
HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84
<html>
<body>Hello World</body>
</html>
即使编码协议也就是 body 是使用二进制编码协议,报文元数据也就是header头的键值对却使用了文本编码,非常占字节数。如上图所使用的报文中有效字节数仅仅占约 30%,也就是70%的时间用于传输元数据废编码。当然实际情况下报文内容可能会比这个长,但是报头所占的比例也是非常可观的。
那么假如我们使用自定义tcp协议的报文如下:
报头占用的字节数也就只有16个byte,极大地精简了传输内容。这也就是为什么后端进程间通常会采用 自定义tcp协议 的 rpc 来进行通信的原因
。
二、不单效率那么简单
- 所谓的效率优势是针对 http1.1协议 来讲的,
http2.0协议 已经优化编码效率问题,像 grpc 这种 rpc 库使用的就是 http2.0协议
。这么来说吧,http容器的性能测试单位通常是kqps,自定义tpc协议则通常是以 10kqps 到 100kqps为基准
。- 简单来说成
熟的 rpc库相对 http容器,更多的是封装了 “服务发现”,"负载均衡",“熔断降级” 一类面向服务的高级特性。
可以这么理解,rpc框架是面向服务的更高级的封装。如果把一个http servlet 容器上封装一层服务发现和 函数代理调用,那它就已经可以做一个rpc框架了。
- 所以为什么要用rpc调用?
- 因为良好的
rpc 调用是 面向服务的封装,针对服务的 可用性 和 效率 等都做了优化。单纯使用http调用则缺少了这些特性。
- 可以这样说:
用http不是因为它性能好,而是因为它普适,随便一个web容器就能跑起来你的应用。
三、RPC 底层是怎么实现的
- 之前有看过几个帖子,评论区有激烈的争吵,主要围绕两点,具体如下:
- HTTP 和 RPC 是同一级别,还是被 RPC 包含?
- Restful 也属于 RPC 吗?Restful不属于RPC
对于以上两个问题,这里用一个图来一一说明:
上图是一个比较完整的关系图,这时我们发现HTTP(图中蓝色框)出现了两次。
- 其中一个是 和 RPC并列的,都是跨应用调用方法的解决方案;
- 另一个则是被RPC包含的,是RPC通信过程的可选协议之一。
- 第一个问题的答案是都对。看指的是哪一个蓝色框。从题主的提问看,既然题主在纠结这两者,应该是指与RPC并列的蓝色框。
- 第二个问题是在问远程过程调用(红色框)是不是包含了Restful(黄色框),这种理解的关键在于对RPC的理解。
RPC字面理解是"远程过程调用",即在一个应用中调用另一个应用的方法。那Restful是满足的,通过它可以实现在一个应用中调用另一个应用的方法。但是,上述理解使得RPC的定义过于宽泛。RPC通常特指在一个应用中调用另一个应用的接口而实现的远程调用,即红色框所指的范围。这样,RPC是不包含Restful的。
因此,第二个问题的答案是Restful不属于RPC。
- 调用其他服务的方法用http还是rpc?
RPC的英文全称是:Remote Procedure Call,翻译为中文叫 “远程过程调用”。其中稍显晦涩的其实就是“过程”,过程其实就是“方法”
。所以,可以把RPC理解为“远程方法调用”。要了解远程过程调用,那先理解过程调用。非常简单,如下图,就是调用一个方法。这太常见了,不多解释。
而在分布式系统中,因为每个服务的边界都很小,很有可能调用别的服务提供的方法。这就出现了服务A 调用 服务B 中方法的需求,即远程过程调用
。要想让服务A 调用 服务B 中的方法,最先想到的就是通过 HTTP 请求实现。
是的,这是很常见的,例如 服务B 暴露 Restful接口,然后让 服务A 调用它的接口。基于Restful的调用方式因为可读性好(服务B暴露出的是Restful接口,可读性当然好)而且HTTP请求可以通过各种防火墙,因此非常不错。然而,如前面所述,基于Restful的远程过程调用有着明显的缺点,主要是效率低、封装调用复杂。当存在大量的服务间调用时,这些缺点变得更为突出。
服务A 调用 服务B 的过程是应用间的内部过程,牺牲可读性提升效率、易用性是可取的。基于这种思路,RPC产生了
。
通常,RPC
要求在调用方中放置被调用的方法的接口
。调用方只要调用了这些接口,就相当于调用了被调用方的实际方法,十分易用
。于是,调用方可以像调用内部接口一样调用远程的方法,而不用封装参数名和参数值等操作。
那要想实现这个过程该怎么办呢?别急,咱们一步一步来。
- 首先,调用方调用的是接口,必须得为接口构造一个假的实现。显然,要使用动态代理。这样,调用方的调用就被动态代理接收到了。
- 动态代理接收到调用后,应该想办法调用远程的实际实现。这包括下面几步:
- 识别具体要调用的远程方法的 IP、端口
- 将调用方法的入参进行序列化
- 通过通信将请求发送到远程的方法中
- 这样,远程的服务就接收到了调用方的请求。它应该:
- 反序列化各个调用参数
- 定位到实际要调用的方法,然后输入参数,执行方法
- 按照调用的路径返回调用的结果
整个过程如下所示。
这样,RPC操作就完成了。调用方调用内部的一个方法,但是被RPC框架偷梁换柱为远程的一个方法。之间的通信数据可读性不需要好,只需要RPC框架能读懂即可,因此效率可以更高
。通常使用UDP或者TCP作为通讯协议,当然也可以使用HTTP。
四、RPC和restful api对比
REST是一种设计风格,它的很多思维方式与RPC是完全冲突的。 RPC的思想是把本地函数映射到API,也就是说一个API对应的是一个function,我本地有一个getAllUsers,远程也能通过某种约定的协议来调用这个getAllUsers。至于这个协议是Socket、是HTTP还是别的什么并不重要
; RPC中的主体都是动作,是个动词
,表示我要做什么。 而REST则不然,它的URL主体是资源,是个名词
。而且也仅支持HTTP协议,规定了使用HTTP Method表达本次要做的动作,类型一般也不超过那四五种。这些动作表达了对资源仅有的几种转化方式。
RPC的根本问题是耦合。RPC客户端以多种方式与服务实现紧密耦合,并且很难在不中断客户端的情况下更改服务实现。RPC更偏向内部调用,REST更偏向外部调用。
Web 服务应该算是 RPC 的一个子集
,理论上 RPC 能实现的功能, 用 Web 服务也能实现,甚至很多 RPC 框架选用 HTTP 协议作为传输层。
现在很多网站的 API 都是以 HTTP 服务的形式提供的,这也算是 RPC 的一种形式。
区别主要在这 2 个东西设计的出发点不太一样:
- HTTP 是面向浏览器设计的应用层协议,操作的核心在资源。我们更多的用 Web 服务在做网站。
- RPC是为了在像在本地调用一个函数那样调用远程的代码而设计的,所以更关注减少本地调用和远程调用的差异,像 SOAP(简单对象访问协议)这种东西是可以把对象当参数传的。
我们讨论 RPC 和 Web 的区别,其实是在谈论 2 个东西:序列化协议和传输协议。序列化协议比如常见的 XML,JSON 和比较现代的 Protocol Buffers、Thrift。 传输协议比如 TCP、UDP 以及更高层的 HTTP 1.1、HTTP 2.0。
一般我们考虑用 RPC 而不是 HTTP 构建自己的服务,通常是考虑到下面的因素:
- 接口是否需要 Schema 约束
- 是否需要更高效的传输协议(TCP,HTTP 2.0)
- 是否对数据包的大小非常敏感
比如 HTTP 是基于文本的协议,头部有非常多冗余(对于 RPC 服务而言)。HTTP 中我们用的最多就是 RESTful ,而 RESTful 是个弱 Schema 约束,大家通过文档沟通
,但是如果我就是不在实现的时候对接口文档约定的参数做检查,你也不能把我怎么样。这个时候 Thrift 这种序列化协议的优势就体现出来了,由于 Schema 的存在,可以保证服务端接受的参数和 Schema 保持一致。
总结就是:RESTful面向资源,以URI对网络资源进行唯一标识,PRC面向函数调用
五、Dubbo与Feign对比
- 实现远程调用的方式
Http接口(web接口、RestTemplate+Okhttp [也就是调用URL]
)、Feign、RPC调用(Dubbo、Socket编程)、Webservice。
- 什么是Feign?
Feign是Spring Cloud提供的一个声明式的伪Http客户端
,它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
Feign默认集成了Ribbon,所以在使用Fegin时,默认就实现了负载均衡的效果。
- 什么是Dubbo?
Dubbo是阿里巴巴开源的基于Java的高性能RPC分布式服务框架
,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
Spring-cloud-alibaba-dubbo是基于SpringCloudAlibaba技术栈对dubbo技术的一种封装,目的在于实现基于RPC的服务调用。
- Feign与Dubbo的对比
Feign与Dubbo功能上有很多类似的地方,因为都是专注于远程调用这个动作。比如注册中心解耦、负载均衡、失败重试熔断、链路监控等。
Dubbo除了注册中心需要进行整合,其它功能都自己实现了,而Feign大部分功能都是依赖全家桶的组件来实现的。
Dubbo小而专一,专注于远程调用。而Spring全家桶而言,远程调用只是一个重要的功能而已。
- 协议支持方面:
Feign更加优雅简单。
Feign是通过REST API实现的远程调用,基于Http传输协议,服务提供者需要对外暴露Http接口供消费者调用,服务粒度是http接口级的。
通过短连接的方式进行通信,不适合高并发的访问。Feign追求的是简洁,少侵入(因为就服务端而言,在SpringCloud环境下,不需要做任何额外的操作。而Dubbo的服务端需要配置开放的Dubbo接口)。Dubbo方式更灵活。Dubbo是通过RPC调用实现的远程调用,支持多传输协议(Dubbo、Rmi、http、redis等等),可以根据业务场景选择最佳的方式,非常灵活。
默认的Dubbo协议:利用Netty,TCP传输,单一、异步、长连接,适合数据量小、高并发和服务提供者远远少于消费者的场景。
Dubbo通过TCP长连接的方式进行通信,服务粒度是方法级的。从协议层选择看,Dubbo是配置化的,更加灵活。Dubbo协议更适合小数据高并发场景。
- 通信性能方面:
- SpringCloud的通信采用Openfeign(feign)组件。
Feign基于Http传输协议,底层实现是rest。从OSI 7层模型上来看rest属于应用层
。 在高并发场景下性能不够理想,成为性能瓶颈(虽然他是基于Ribbon以及带有熔断机制可以防止雪崩),需要改造。具体需要改造的内容需要时再研究。- Dubbo框架的通信协议采用RPC协议,属于传输层协议,性能上自然比rest高。提升了交互的性能,保持了长连接,高性能。
Dubbo性能更好,比如支持异步调用、Netty性能更好。
Dubbo主要是配置而无需改造。
- 负载均衡方面:
- Feign默认使用Ribbon作为负载均衡的组件。
- Dubbo和Ribbon(Feign默认集成Ribbon)都支持负载均衡策略,但是Dubbo支持的更灵活。 Dubbo和Ribbon对比:
Ribbon的负载均衡策略:随机、规则轮询、空闲策略、响应时间策略。
- Dubbo的负载均衡策略:
Dubbo支持4种算法,随机、权重轮询、最少活跃调用数、一致性Hash策略。
而且算法里面引入权重的概念。
Dubbo可以使用路由策略,然后再进行负载均衡。 Dubbo配置的形式不仅支持代码配置,还支持Dubbo控制台灵活动态配置。 Dubbo负载均衡的算法可以精准到某个服务接口的某个方法,而Ribbon的算法是Client级别的。Ribbon需要进行全局配置,个性化配置比较麻烦。
- 总结
Dubbo支持更多功能、更灵活、支持高并发的
RPC框架。
SpringCloud全家桶里面(Feign、Ribbon、Hystrix),特点是非常方便。Ribbon、Hystrix、Feign在服务治理中,配合Spring Cloud做微服务,使用上有很多优势,社区也比较活跃,看将来更新发展。
业务发展影响着架构的选型,当服务数量不是很大时,使用普通的分布式RPC架构即可,
当服务数量增长到一定数据,需要进行服务治理时,就需要考虑使用流式计算架构。Dubbo可以方便的做更精细化的流量调度,服务结构治理的方案成熟
,适合生产上使用,虽然Dubbo是尘封后重新开启,但这并不影响其技术价值。如果项目对性能要求不是很严格,可以选择使用Feign,它使用起来更方便。
如果需要提高性能,避开基于Http方式的性能瓶颈,可以使用Dubbo
。 Dubbo SpringCloud的出现,使得Dubbo既能够完全整合到Spring Cloud的技术栈中,享受Spring Cloud生态中的技术支持和标准化输出,又能够弥补Spring Cloud中服务治理这方面的短板。