侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

微服务之间的远程调用方式

孔子说JAVA
2022-08-12 / 0 评论 / 0 点赞 / 63 阅读 / 6,608 字 / 正在检测是否收录...

在微服务架构中,需要调用很多服务才能完成一项功能。服务调用者与服务提供者之前通过远程调用技术进行交互,目前常用的远程调用技术主要有两种:RPC和Restful。其中RPC主流框架有Dubbo、gRPC等,Restful主流框架为spring boot、spring cloud、Dropwizard。

image-1660263386306

1、RPC

RPC(Remote Procedure Call)翻译为远程过程调用,是一种进程间通信方式,允许像调用本地服务那样调用远程服务,其主要目标就是让远程调用更简单、透明,负责屏蔽底层的传输方式(TCP/UDP)、序列化方式和通信细节。开发人员只需要知道服务在哪里,提供了哪些接口,而不需要了解底层网络技术的协议。在OSI网络通信模型中,RPC跨越了传输层和应用层,RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

  • 远程过程调用,就是两个服务A、B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
  • RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)。
  • RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)。

1.1 RPC 框架的核心组件

  • 客户端(Client):服务调用方。
  • 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
  • 服务端存根(Server Stub):接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理。
  • 服务端(Server):服务的真正提供者。

1.2 RPC 的调用流程

image-1660264013176

  1. 服务消费者(Client 客户端)通过本地调用的方式调用需要消费的服务;
  2. 客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体;
  3. 客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端;
  4. 服务端存根(Server Stub)收到消息后进行解码,反序列化操作;
  5. 服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理;
  6. 服务端(Server)执行具体的业务逻辑,并将处理结果返回给服务端存根(Server Stub);
  7. 服务端存根(Server Stub)将返回结果序列化,并通过网络发送给消费方;
  8. 客户端存根(Client Stub)接收到消息,并进行解码与反序列化;
  9. 服务消费方得到最终结果。

1.3 RPC 的优缺点

优点:

  • 调用简单,清晰,透明,不用像 rest 一样复杂,就像调用本地方法一样简单。
  • 高效低延迟,性能高。
  • 自定义协议(让传输报文提及更小)。
  • 性能消耗低,高效的序列化协议可以支持高效的二进制传输。
  • 自带负载均衡。

缺点:

  • 耦合性强。

1.4 RPC 主流框架

目前流行的开源RPC框架还是比较多的,使用比较多的有阿里巴巴的Dubbo、Facebook的Thrift、Google的gRPC等。

● Dubbo:阿里巴巴开源的一个RPC框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都具备可插拔等特性,应用可通过高性能的RPC实现服务的输出和输入功能,它可以和Spring框架无缝集成。官网地址:https://dubbo.apache.org/zh/index.html 。基于TCP协议。

● gRPC:Google开发的高性能、通用的开源RPC框架。gRPC由Google面向移动应用开发,并基于HTTP2协议标准设计。它基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。

● Thrift:Facebook开源的RPC框架,它是一个跨语言的服务开发框架。应用对于底层的RPC通信是透明的。不过对于用户来说需要学习特定领域语言,还是有一定成本的。

● Motan:新浪微博开源的一个Java框架。Motan在微博平台中已经广泛应用,每天为数百个服务完成近千亿次的调用。与Dubbo相比,Motan在功能方面并没有那么全面,也没有实现特别多的扩展,对跨语言调用支持较差,主要支持Java。

● Hessian:Hessian是一个Web Service框架,支持RPC调用,功能简单,使用起来也方便。它采用二进制RPC协议,基于HTTP进行传输,通过Servlet提供远程服务,可以通过Hessain提供的API来发起请求和接收请求。

● Spring Cloud:Pivotal公司在2014年对外开源的RPC框架,它仅支持Java,最近几年生态发展得比较好,是Java领域目前事实上的微服务框架标准。基于HTTP协议。官网地址:https://spring.io/projects/spring-cloud

RPC框架的功能比较

功能 Hessian Montan gRpc Thrift Dubbo Spring Cloud
开发语言 跨语言 java 跨语言 跨语言 java java
服务治理 Χ Χ Χ
多序列化框架支持 Hessian √(支持Hessian2、JSON,可扩展) Χ(只支持ProtoBuf) Χ(Thrift格式)
多种注册中心 Χ Χ Χ
管理中心 Χ Χ Χ
跨编程语言 Χ(支持PHP和C) Χ Χ
支持REST Χ Χ Χ Χ Χ
关注度
上手难度
运维成本

通过上面不同功能的对比可以看到,不同的RPC框架有不同的适用场景。如果你更加关注服务治理,这类框架能够提供包括服务注册、管理中心在内的整套的微服务技术架构支持,典型代表有Spring Cloud、Dubbo、Montan。如果你更关注跨语言服务调用,典型代表有Hessian、gRPC、Thrift。

2、Restful

REST是一种架构风格,指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。REST规范把所有内容都视为资源,网络上一切皆资源。

REST并没有创造新的技术,组件或服务,只是使用Web的现有特征和能力。可以完全通过HTTP协议实现,使用 HTTP 协议处理数据通信。REST架构对资源的操作包括获取、创建、修改和删除资源的操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。在服务调用之间使用的是种基于HTTP实现的RestFul技术,通过在服务提供者中暴露一个可以请求的地址来实现。

2.1 统一数据操作接口

RESTful架构风格规定,数据的元操作,即CRUD(create, read, update和delete,即数据的增删查改)操作,分别对应于HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口,仅通过HTTP方法,就可以完成对数据的所有增删查改工作。即:

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)。
  • DELETE(DELETE):从服务器删除资源。

2.2 无状态URI

可以用一个URI(统一资源定位符)指向资源,即每个URI都对应一个特定的资源。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或识别符。一般每个资源至少有一个URI与之对应,最典型的URI即URL。

所谓无状态URI,即所有的资源都可以通过URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而改变。

  • 有状态和无状态的区别,举个简单的例子说明一下。如查询员工的工资,如果查询工资是需要登录系统,进入查询工资的页面,执行相关操作后,获取工资的多少,则这种情况是有状态的,因为查询工资的每一步操作都依赖于前一步操作,只要前置操作不成功,后续操作就无法执行;如果输入一个url即可得到指定员工的工资,则这种情况是无状态的,因为获取工资不依赖于其他资源或状态,且这种情况下,员工工资是一个资源,由一个url与之对应,可以通过HTTP中的GET方法得到资源,这是典型的RESTful风格。

2.3 Restful 的优缺点

优点:

  • 耦合性低,兼容性好,提高开发效率,不用关心接口实现细节,相对更规范,更标准,更通用,跨语言支持

缺点:

  • 性能不如 RPC 高。

2.4 Restful 主流框架

● Dropwizard 提供了稳定成熟的Java库, 并封装成一个简单的轻量级的包。Dropwizard 介于框架和库之间。它提供了一个开发web应用程序的全部所需。由于内置模块化,一个应用程序可以保持小而精干的特点,减少开发和维护的时间,减少负担。Dropwizard 使用已有的 Jetty HTTP 库,嵌入到你的项目中,无需外部的server。所有的Dropwizard项目都有一个 main 方法来管理内建的 HTTP server。

● spring boot/spring cloud不仅支持RPC,还提供了对Restful的支持。

3、REST和RPC的比较

RPC:远程过程调用 (面向方法)
SOA:面向服务的架构(面向消息)
REST:Representational state transfer (面向资源)

RPC实现服务间调用的痛点:

  1. RPC服务提供方与调用方接口依赖方式太强,会导致编码的复杂性,而REST接口相比RPC更为轻量化,服务提供方和调用方的依赖只是依靠一纸契约,不存在代码级别的强依赖。
  2. RPC服务对平台敏感,难以简单复用。
  3. 而REST可以实现跨平台,任何一个语言的调用方都可以根据接口定义来实现。

RestFul和RPC的区别

  1. 从本质区别上看,RPC是基于TCP实现的,RESTFUL是基于HTTP来实现的。
  2. 从传输速度上来看,因为HTTP封装的数据量更多所以数据传输量更大,所以RPC的传输速度是比RESTFUL更快的。
  3. 因为HTTP协议是各个框架都普遍支持的。在toC情况下,因为不知道情况来源的框架、数据形势是什么样的,所以在网关可以使用Restful利用http来接受。而在微服务内部的各模块之间因为各协议方案是公司内部自己定的,所以知道各种数据方式,可以使用TCP传输以使各模块之间的数据传输更快。所以可以网关和外界的数据传输使用RESTFUL,微服务内部的各模块之间使用RPC。
  4. RESTFUL的API的设计上是面向资源的,对于同一资源的获取、传输、修改可以使用GET、POST、PUT来对同一个URL进行区别,而RPC通常把动词直接体现在URL上。
比较项 RESTful RPC
耦合性 松散耦合 强耦合
消息协议 文本型XML,JSON 二进制thrift、protobuf、avro
通信协议 HTTP/HTTP2 TCP为主,也可以使HTTP
性能 一般低于RPC
灵活度
接口契约IDL Swagger Thrift、protobuf idl
客户端 一般http client可访问,也支持多语言 强类型客户端,一般自动生成,可支持多语言
代表 springboot/mvc、jax-rs dubbo、motan、tars、grpc、thrift
开发者友好 文本消息开发者可读,浏览器可直接访问查看结果 客户端比较方便,二进制消息不可读
对外开放 直接对外开放 需要转换成REST/文本协议
使用场景 需要调用到第三方服务的 各个服务都是内部开发的

rest与rpc适用场景

  • RPC 主要用于公司内部的服务调用,性能消耗低,传输效率高,实现复杂。
  • REST 主要用于对外的异构环境,浏览器接口调用,App 接口调用,第三方接口调用等。
  • IO 密集的服务调用用 RPC,低频服务用 REST。。
  • 服务调用过于密集与复杂,RPC 就比较适用。

RPC 使用场景(大型的网站,内部子系统较多、接口非常多的情况下适合使用 RPC):

  • 长链接。不必每次通信都要像 HTTP 一样去 3 次握手,减少了网络开销。
  • 注册发布机制。RPC 框架一般都有注册中心,有丰富的监控管理、发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。
  • 安全性,没有暴露资源操作。

例如:

我们为每个微服务定义了各自的 service 抽象接口,并通过持续集成发布到私有仓库中,调用方应用对微服务提供的抽象接口存在强依赖关系,因此不论开发、测试、集成环境都需要严格的管理版本依赖,才不会出现服务方与调用方的不一致导致应用无法编译成功等一系列问题,以及这也会直接影响本地开发的环境要求,往往一个依赖很多服务的上层应用,每天都要更新很多代码并 install 之后才能进行后续的开发。若没有严格的版本管理制度或开发一些自动化工具,这样的依赖关系会成为开发团队的一大噩梦。

而 REST 接口相比 RPC 更为轻量化,服务提供方和调用方的依赖只是依靠一纸契约,不存在代码级别的强依赖,当然 REST 接口也有痛点,因为接口定义过轻,很容易导致定义文档与实际实现不一致导致服务集成时的问题,但是该问题很好解决,只需要通过每个服务整合swagger,让每个服务的代码与文档一体化,就能解决。所以在分布式环境下,REST 方式的服务依赖要比 RPC 方式的依赖更为灵活。

4、CAP原理

分布式系统最大的难点就是各个节点的状如何同步,在开发一个分布式系统时都通过CAP三个方向来设计此系统。

C:Consistency

  • 即一致性,对于任何从客户端发送到分布式系统的数据读取请求,要么读到最新的数据要么失败。换句话说,一致性是站在分布式系统的角度,对访问本系统的客户端的一种承诺:要么我给您返回一个错误,要么我给你返回绝对一致的最新数据,不难看出,其强调的是数据正确。这需要确保分布式系统能实时同步各节点之间的数据。

A:Availability

  • 即可用性,对于任何求从客户端发送到分布式系统的数据读取请求,都一定会收到数据,不会收到错误数据,但不保证客户端收到的数据一定是最新的数据。换句话说,可用性是站在分布式系统的角度,对访问本系统的客户的另一种承诺:我一定会给您返回数据,不会给你返回错误数据,但不保证数据最新,强调的是不出错。

P: Partition Tolerance

  • 即分区容忍性,这里的分区是指网络意义上的分区。由于网络是不可靠的,所有节点之间很可能出现无法通讯的情况,在节点不能通信时,要保证系统可以继续正常服务,强调的是不挂掉。分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择

一个数据分布式系统不可能同时满足C和A和P这3个条件。所以系统架构师在设计系统时,不要将精力浪费在如何设计能满足三者的完美分布式系统,而是应该进行取舍。由于网络的不可靠性质,大多数开源的分布式系统都会实现P,也就是分区容忍性,之后在C和A中做抉择。

CAP原理简单证明,有如下设计:

image-1660268253970

刚开始结点A中 a=1,假如收到一个变更请求,结果A中的a=2。接下来A应该将数据同步到B中,使B中a=2,即实现数据一致性。3个场景分别证明:

满足C和P

在数据同步时,即将B中的a置为2时,由于网络的问题,使得A和B之间的通信出现了延迟,但此时B收到了一个读取a的请求,但此时a还不是最新的,因此只有等,一直到a更新完,这就导致了客户端一直拿不到返回结果,即系统的可用性比较差,即违反了A。
所以,在保证C和P的情况下,是无法同时保证A的。

满足A和P

为了保证客户端能及时响应,即高可用性,所有请求必须在有限时间内返回。同样在数据同步时,需要将B中的a置为2时,由于网络的问题,使得A和B之间的通信出现了延迟,但此时B收到了一个读取a的请求,但为了在有限时间内必须给出响应,所以只能将旧数据返回给客户端,即违反了C。
所以,在保证C和P的情况下,是无法同时保证C的。

满足A和C

如果要保证高可用和一致性,只有在网络情况良好且可靠的情况下才能实现。这样A才能立即将更新消息发送给B。但是网络情况不能保证,出现延迟,阻塞,丢包等现象。所以要满足A和C,只有将结点A和结点B放到一个区内才可以(可以理解为将数据副本存放在同一个服务器上),也就丧失了P这个保证,这样的话已经不算是分布式系统了。

所以,在保证C和A的情况下,是无法同时保证P的。

0

评论区