无论是微服务还是分布式服务(都是SOA,都是面向服务编程),都面临着服务间的远程调用。之前只会使用相关框架,但其实没有体系的了解过这一块内容,从这篇Blog开始详细学习远程调用的方式、原理、常用框架,基于两个基本大的分类:HTTP和RPC来进行学习,本篇主要是进行综合性概述,分别了解两种不同方式的基本原理和常用框架,最后依据使用场景进行一个讨论。
RPC调用方式
RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易
说得通俗一点就是:A计算机提供一个服务,B计算机可以像调用本地服务那样调用A计算机的服务
-
实现远程调用其他计算机的服务,要实现远程调用,肯定是通过网络传输数据。A程序提供服务,B程序通过网络将请求参数传递给A,A本地执行后得到结果,再将结果返回给B程序。这里需要关注的有两点:
- 采用何种网络通讯协议?现在比较流行的RPC框架,都会采用TCP作为底层传输协议
- 数据传输的格式怎样?两个程序进行通讯,必须约定好数据传输格式。就好比两个人聊天,要用同一种语言,否则无法沟通。所以,我们必须定义好请求和响应的格式。另外,数据在网路中传输需要进行序列化,所以还需要约定统一的序列化的方式。
-
像调用本地服务一样调用远程服务,如果仅仅是远程调用,还不算是RPC,因为RPC强调的是过程调用,调用的过程对用户而言是应该是透明的,用户不应该关心调用的细节,可以像调用本地服务一样调用远程服务。所以RPC一定要对调用的过程进行封装
虽然不同的RPC框架实现方式不同,但是封装的基本范式类似,封装的基本范式如下:
RPC基本原理
两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。为什么要用RPC呢?就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如不同的系统间的通讯,甚至不同的组织间的通讯,由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用。RPC就是要像调用本地的函数一样去调远程函数
在上述图中,通过1-10的步骤图解的形式,说明了RPC每一步的调用过程。
客户端发起网络调用
- 客户端想要发起一个远程过程调用,首先通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
- 客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化操作,并打包成数据包。
网络传递数据
- 客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端。
服务端接收到调用:
- 服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息。
- 服务端Stub程序准备相关数据,调用本地Server对应的功能方法进行,并传入相应的参数,进行业务处理。
服务端生成调用结果:
- 服务端程序根据已有业务逻辑执行调用过程,待业务执行结束,将执行结果返回给服务端Stub程序。
- 服务端Stub程序将程序调用结果按照约定的协议进行序列化,并通过网络发送回客户端Stub程序。
网络传递数据
- 服务端Stub查找到远程客户端程序的IP地址,调用Socket通信协议,通过网络发送给客户端。
客户端收到响应
- 客户端Stub程序接收到服务端Stub发送的返回数据,对数据进行反序列化操作,并将调用返回的数据传递给客户端请求发起者。
- 客户端请求发起者得到调用结果,整个RPC调用过程结束
从RPC调用中我们发现至少需要生产者(服务提供者)和消费者(服务调用者)两个角色。
从实现技术上分为三层:代理层、序列化层和网络层,对于消费者:
- 代理层:消费者将对应的接口,通过RPC框架的代理来生成一个对象到Spring容器中。代理层将代理接口生成该接口的对象,该对象处理调用时传过来的对象、方法、参数,通过序列化层封装好,调用网络层。
- 序列化层:将请求的参数序列化成报文;将返回的报文反序列化成对象;
- 网络层: 将报文与服务端通信;接收返回结果
对于生产者而言:
- 代理层:一个应用提供服务,必须由一个网络监听的模块,这个模块大多有开源的容器来处理网络上的监听;服务需要注册,只有注册了的服务才可以被调用;注册的服务需要被我们发射调用到,来进行相应的处理。
- 序列化层: 就是相应的做请求的反序列化和结果的序列化。
- 网络层:接收客户端报文;将序列化的结果返回给客户端
所以从上述描述中我们发现,完整的流程其实还需要监听器和注册中心,这在不同的框架中有不同的实现方式,例如Dubbo使用Zookeeper做注册中心
RPC常用框架
RPC常用框架分为两类:一类是跟某种特定语言平台绑定的,另一类是与语言无关即跨语言平台的。跟语言平台绑定的开源 RPC 框架主要有下面几种:
- Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并于 2011 年末对外开源,仅支持 Java 语言。
- Motan:微博内部使用的 RPC 框架,于 2016 年对外开源,仅支持 Java 语言。
- Tars:腾讯内部使用的 RPC 框架,于 2017 年对外开源,仅支持 C++ 语言。
- Spring Cloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,仅支持 Java 语言
而跨语言平台的开源 RPC 框架主要有以下几种。
- gRPC:Google 于 2015 年对外开源的跨语言 RPC 框架,支持多种语言。
- Thrift:最初是由 Facebook 开发的内部系统跨语言的 RPC 框架,2007 年贡献给了 Apache 基金,成为 Apache 开源项目之一,支持多种语言。
如果你的业务场景仅仅局限于一种语言的话,可以选择跟语言绑定的 RPC 框架中的一种。如果涉及多个语言平台之间的相互调用,就应该选择跨语言平台的 RPC 框架。这几种里我会重点学习和介绍Dubbo和Spring Cloud,如果有多余精力,会学习一下gRPC和Thrift。更多关于这几种框架的对比:RPC框架对比
HTTP调用方式
http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。现在热门的Rest风格,就可以通过http协议来实现。其实这个无需做过多的介绍了,HTTP在我之前的多篇Blog中均有提到,尤其是这篇:【计算机网络基础 六】应用层
HTTP基本原理
Http协议,又叫超文本传输协议,是一种应用层协议。规定了网络传输的请求格式、响应格式、资源定位和操作的方式等。但是底层采用什么网络传输协议,并没有规定,不过现在都是采用TCP协议作为底层传输协议。Http与RPC的远程调用非常像,都是按照某种规定好的数据格式进行网络通信,有请求,有响应。
其实我们之前一直学习的是服务端代码如何编写以及Tomcat上如何部署服务端代码以及代码如何发挥作用,看上图比较简单,实际上很复杂,感兴趣可以参照之前的Blog: 深入理解Tomcat系统架构及工作原理,【Java Web编程 八】深入理解Servlet常用对象,这两篇Blog详细介绍了服务端Servlet代码如何编写,以及Tomcat的工作原理,为何服务端代码部署到Tomcat上能发挥作用。
HTTP常用框架
不同于RPC框架中生产端服务端我们都需要重新配置,通常我们对外封装的Controller接口其实就可以充当服务端,也就是被调用方。所以HTTP的框架这里只要了解调用方的框架即可,其实也就是HTTP的客户端:
- HttpURLConnection:JDK自带的HTTP请求客户端。
- HttpClient:HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口(基于Http协议的),提高了开发的效率,也方便提高代码的健壮性。
- OkHttp :OkHttp是一个高效的 HTTP 库,它实现了几乎和java.net.HttpURLConnection一样的API,现在最新版本为OkHttp3
- Retrofit: Retrofit是一个非常好的优化体验的框架,其核心还是调用OkHttp框架进行网络接口请求,其最大的好处就是让我们调用网络接口就像调用普通接口一样方便,通过各种注解方便的进行网络请求,目前最新版本为Retrofit2。
不同于RPC框架的多样选择,我建议HTTP框架直接使用Retrofit,它吸收了OkHttp的所有好处,还帮我们做了最大程度的封装,使用起来非常方便
RPC和HTTP的区别
二者之间的核心区别就是,RPC基于TCP协议,有一定侵入性,但性能高;HTTP基于HTTP协议,侵入性低,性能低。既然两种方式都可以实现远程调用,我们该如何选择呢?
- 速度来看,RPC要比http更快,虽然底层都是TCP,但是http协议的信息往往比较臃肿
- 难度来看,RPC实现较为复杂,http相对比较简单
- 灵活性来看,http更胜一筹,因为它不关心实现细节,跨平台、跨语言。
因此,两者都有不同的使用场景:
- 如果对效率要求更高,并且开发过程使用统一的技术栈,那么用RPC还是不错的。
- 如果需要更加灵活,跨语言、跨平台,显然http更合适
其实微服务,更加强调的是独立、自治、灵活。而RPC方式的限制较多,因此微服务框架中,一般都会采用基于Http的Rest风格服务。从成本上考虑,如果从一个服务需要调用的接口太多或者入参及响应Model过于复杂,还是建议RPC,因为HTTP的封装代码需要写太多了,反之,则无脑用HTTP,且要用Retrofit2。
总结一下
无论是RPC还是HTTP,它们都是远程访问的一种方式,基于不同的网络层级,所以优缺点也显而易见,RPC直接基于传输层的TPC协议,所以性能更好,但是RPC要求有注册中心,客户端服务端语言不同需要做兼容(例如thrift和gRpc框架可以进行语言兼容);HTTP基于应用层的HTTP协议,socket传输时多做一层转化,性能差一些,但是HTTP一般不需要注册中心,客户端服务端语言无关,只要能接收和发送HTTP请求即可。