《微服务架构设计模式》之三:微服务架构中的进程通信

概述

交互方式

客户端和服务端交互方式可以从两个维度来分:

  • 维度1:一对一和多对多
    一对一:每个客户端请求由一个实例来处理。
    一对多:每个客户端请求由多个实例来处理。
  • 维度2:同步和异步
    同步模式:客户端请求需要等待服务端响应,客户端等待响应时可能导致阻塞。
    异步模式:客户端请求不需要服务端实时响应,服务端可以非实时响应,客户端的请求不会阻塞进程。
    一对一交互方式的几种类型:请求/响应、异步请求/响应、单向通知
    一对多交互方式的几种类型:
  • 发布/订阅方式:客户端发布通知消息,被零个或多个感兴趣的服务订阅。
    • 发布/异步响应方式:客户端发布请求消息,然后等待感兴趣的服务发回响应。
      请求/响应、异步请求/响应、单向通知。

消息的格式

进程通信的本质是交互消息,消息通常包含数据。所以一个重要的设计决策就是要设计消息的格式。
消息的格式可以分为两大类:文本二进制

基于文本的消息格式

基于文本的消息格式有JSON和XML。
这种格式的消息好处在于它的可读性很高,同时也是自描述(啥意思?就是字段名可以说明它自己是什么意思吧)。JSON是命名属性的集合,XML是命名元素和值得集合。这样的消息格式可以消息接受方只挑选出他们感兴趣的值,对消息结构的修改可以做到很好的后向兼容性。
使用基于文本格式消息的弊端主要是消息往往过度冗长,特别是 XML。消息的每一次传递都必须反复包含除了值以外的属性名称,这样会造成额外的开销。(确实是这样,用过的都知道0.0)。另外一个弊端是解析文本引入的额外开销,尤其是在消息较大的时候。因此,在对效率和性能敏感的场景下,你可能需要考虑基于二进制格式的消息。

基于文本的消息格式

二进制格式消息作者也写了两段,主要是平时从没用过这种格式,这里就不写了~

使用断路器模式处理局部故障

当客户端向服务端发送同步请求时,永远会面临服务端局部故障的风险。故障的原因可能是服务器挂了、正在维护或者因为负载过多响应缓慢。这时候服务端无法在有限的时间内给客户端回应,客户端将会阻塞,同时又接受新的请求导致资源耗尽,无法处理请求。例如下图调研链路中,移动应用调用Create order端的,再掉OrderServece代理,再掉用OrderService,如果OrderService挂了,OrderService代理会一直等下去,最终API GateWay资源耗尽,整个API GateWay将不可用。
在这里插入图片描述
所以我们需要合理的设计服务来防止故障在整个应用程序中的传导和扩散。解决这个问题分为两个部分

  • 必须让远程调用代理用正确处理无响应服务的能力。(作为调用方,自己别受无响应服务的影响,先保护好自己,别自己也搞挂了)
  • 需要决定如何从失败的远程服务中恢复。

开发远程可靠的远程过程调用代理

当一个服务同步调用另一个服务时,它应该使用Netflix 描述的方法(http://techblog.netflixcom/2012/02/faulttolerance-in-high-volumehtml)来保护自己。(啊 网飞还干这个?不是同一个吧 哈哈!)这种方法包括以下机制的组合。

  • 网络超时:在等待针对请求的响应时,一定不要做成无限阻塞,而是要设定一个超时。使用超时可以保证不会一直在无响应的请求上浪费资源。
  • 限制客户端向服务器发出请求的数量:把客户端能够向特定服务发起的请求设置一个上限,如果请求达到了这样的上限,很有可能发起更多的请求也无济于事,这时就应该让请求立刻失败。
  • 断路器模式:监控客户端发出请求的成功和失败数量,如果失败的比例超过一定的闽值,就启动断路器,让后续的调用立刻失效。如果大量的请求都以失败而告终,这说明被调服务不可用,这样即使发起更多的调用也是无济于事。在经过一定的时间后客户端应该继续尝试,如果调用成功,则解除断路器。其实就是我们经常提到的"熔断"。

从失败的远程服务中恢复

未完待续

使用服务发现

客户端向服务端发送请求的时候,需要知道服务端的网络地址,就是IP+端口。在以前,传统的应用程序是部署在物理机上,网络地址通常是静态的。所以客户端可以从配置文件中读取网络地址。但现在是基于云的微服务应用程序,其更具有动态性。确实,现在部署很多用的docker了。服务实例具有动态分配的网络位置。此外,由于自动扩展、故障和升级,服务实例集会动态更改。因此,客户端代码必须使用服务发现。
在这里插入图片描述

什么是服务发现

正如刚才所见,你无法使用服务的IP地址静态配置客户端。相反,应用程序必须使用动态发现机制。服务发现的关键组件是服务注册表,它是包含服务实例网络位置信息的一个数据库。
服务实例启动和停止时,服务发现机制会更新服务注册表。当客户端调用服务时,服务发现机制会查询服务注册表以获取可用服务实例的列表,并将请求路由到其中一个服务实例。
实现服务发现有以下两种主要方式:

  • 服务及其客户直接与服务注册表交互
  • 通过部署基础设施来处理服务发现
应用服务发现模式

在这里插入图片描述
这种服务发现方法时两种模式的组合:自注册模式和客户发现模式。
自注册模式就是服务实例向服务注册表注册自己。
客户端发现模式就是客户端从服务列表检索可用服务实例列表。并在他们之间进行负载均衡。

平台服务发现模式

使用应用端服务发现模式,还需要客户端和服务端写一些发现和注册的代码,是有一些耦合的。而使用平台发现模式就简单了。
现在许多现代部署平台(如 Docker 和 Kubernetes)都具有内置的服务注册表和服务发现机制。部署平台为每个服务提供DNS 名称、虚IP (VIP)地址和解析为VIP 地址的DNS 名称。客户端向 DNS 名称和VIP 发出请求,部署平台自动将请求路由到其中一个可用服务实例。因此,服务注册、服务发现和请求路由完全由部署平台处理。如下:在这里插入图片描述
这种发现使用了2种模式:
第三方注册:服务实例有第三方观察注册到服务注册表。不用服务端自己注册了。
服务端发现:客户端向路由器发出请求,路由器负责服务发现。
由平台提供服务发现机制的主要好处是服务发现的所有方面都完全由部署平台处理。服务和客户端都不包含任何服务发现代码。因此,无论使用哪种语言或框架,服务发现机制都可供所有服务和客户使用。

基于异步消息模式的通信

使用消息机制时,服务之间的通信是采用异步消息的方式完成。有的是使用消息代理,有的不使用消息代理。由于通信是异步的,因此客户端不会堵塞和等待回复,相反客户端都假定消息不会马上就收到。

什么是消息传递

消息是由消息头部和消息主体组成。

消息有几种不同类型的消息。

  • 文档:仅包含数据的通用消息。接收者决定如何解释它。对命令消息的回复就是文档消息的一种使用场景。
  • 命令:一条等同于RPC请求的消息,指定要调用的操作及其参数。
  • 事件:表示发送方这一端发生了重要的事件。事件通常是领域事件,表示领域对象的状态变更。

消息通道

在这里插入图片描述
两种类型的消息通道

  • 点对点通道向正在从、。通道读取的一个消费者传递消息。服务使用点对点通道实现前面描述的一对一交互方式。
  • 发布-订阅通道将一条消息发给所有订阅的接收方。服务使用发布-订阅通道来实现前面描述的一对多交互方式。

实现单向通知

客户端将消息(通常是命令式消息)发送到服务所拥有的点对点通道。服务订阅该通道并处理该消息,但服务不会发回回复。

实现发布订阅

实现发布/异步响应

使用消息代理

在这里插入图片描述
无代理架构中,服务可以直接交互消息。
好处

  • 允许更轻的网络流量和更低的延迟。因为消息直接从发送方发送到接收方,不必经过代理转发。
  • 清除了消息代理可能成为性能瓶颈或单点故障的可能性。
  • 具有较低的操作复杂性,因为不需要设置和维护消息代理。

弊端

  • 服务需要了解彼此的位置,因此必须使用服务发现机制。
  • 这样可能会导致可用性降低,因为在交换消息时,消息的发送方和接收方都必须同时在线
  • 在实现例如确保消息能够成功投递这些复杂功能时的挑战性更大。
    由于以上限制,大多数企业应用程序使用基于消息代理的架构。

基于代理的消息

消息代理是所有消息的中介节点。发送方将消息写入消息代理,消息代理将消息发送给接收方。使用消息代理的一个重要好处是发送方不需要知道接收方的网络位置,另一个好处是消息代理缓冲消息,直到接收方能够处理它们。
流行的开源消息代理包括Apache ActiveMQ、RabbitMQ、Apache Kafka 。(第一个不了解,其他两个用过)
还有基于云的消息服务,例如AWS Kinesis(https://awsamazon.com/kinesis)和AWSsos(https://aws.amazon.com/sqs/)。
选择消息代理时,你需要考虑以下各种因素:

  • 支持的编程语言:你选择的消息代理应该支持尽可能多的编程语言。
  • 支持的消息标准:消息代理是否支持多种消息标准,比如AMQP和STOMP,还是它仅支持专用的消息标准?
  • 消息排序:消息代理是否能够保留消息的排序?
  • 投递保证:消息代理提供什么样的消息投递保证?
  • 持久性:消息是否持久化保存到磁盘并且能够在代理崩溃时恢复?
  • 耐久性:如果接收方重新连接到消息代理,它是否会收到断开连接时发送的消息?
  • 可扩展性:消息代理的可扩展性如何?
  • 延迟:端到端是否有较大延迟?
  • 竞争性(并发)接收方:消息代理是否支持竞争性接收方?

基于消息代理的好处和弊端

好处

  • 松耦合:客户端发起请求时只要发送给特定的通道即可,客户端完全不需要感知服务实例的情况,客户端不需要使用服务发现机制去获得服务实例的网络位置。
  • 消息缓存:消息代理可以在消息被处理之前一直缓存消息。像HTTP这样的同步请求响应协议,在交换数据时,发送方和接收方必须同时在线。然而,在使用消息机制的情况下,消息会在队列中缓存,直到它们被接收方处理。
  • 灵活的通信:消息机制支持前面提到的所有交互方式
  • 明确的进程间通信:基于RPC的机制总是企图让远程服务调用跟本地调用看上去没什么区别(在客户端和服务端同时使用远程调用代理)。然而,因为物理定律(如服务器不可预计的硬件失效)和可能的局部故障,远程和本地调用还是大相径庭的。消息机制让这些差异变得很明确,这样程序员不会陷人一种“太平盛世”的错觉。

弊端

  • 潜在的性能瓶颈:消息代理可能存在性能瓶颈。幸运的是,许多现代消息代理都支持高度的横向扩展(就是加机器的意思)。
  • 潜在的单点故障:消息代理的高可用性至关重要,否则系统整体的可靠性将受到影响。幸运的是,大多数现代消息代理都是高可用的。
  • 额外的操作复杂性:消息系统是一个必须独立安装、配置和运维的系统组件。现在我们来深入看看基于消息的架构可能会遇到的一些设计难题。

处理并发消息和消息顺序

消息接收方通常是多个实例处理消息,即便单个服务实例也可能用多个线程同时处理消息。所以带来的挑战就是如何保证每个消息只被处理消息,且按发来顺序处理。如消息发送方发来3个消息,创建订单、更新订单、取消订单,消费方要按顺序处理,且每个消息处理一次。
现代消息代理常用的解决方案是使用分片(分区)通道。

  • 分片通道由两个或多个分片组成,每个分片的行为类似于一个通道。
  • .发送方在消息头部指定分片键,通常是任意字符串或字节序列。通过计算分片键的散列来选择分片。
  • 消息代理将接收方的多个实例组合在一起,并将它们视为相同的逻辑接收方。例如,Apache Kafka 使用术语消费者组。消息代理将每个分片分配给单个接收器。它在接收方启动和关闭时重新分配分片。
    简单来说就是把消息放到一个通道里,只会被一个接收器消费。有顺序的消息放同一个通道,可以保证顺序。如按orderId进行分片。
    在这里插入图片描述

处理重复消息

理想情况下,消息应该保证有且仅有一次传递。(就是kafka中提到的“精准一次”),但是这种传递方式成本非常高,大多数消息代理承诺至少成功传递一次。这时候就可能接收方有可能会收到重复消息,有两种方式可以处理它

  • 编写幂等消息处理程序
  • 跟踪消息并丢弃重复项

编写幂等消息处理器

工作中感觉这种用的最多,如创建订单,一般先查再插 + 订单号作为唯一索引,利用数据库保证幂等。

跟踪消息并丢弃重复消息

未完待续。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/124881.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

maven 上传本地jar包到nexus

maven上传命令 mvn deploy:deploy-file -DgroupIdcom.microsoft.sqlserver -DartifactIdsqljdbc4 -Dversion4.0 -Dpackagingjar -DfileC:\java\top-sdk-java-1.0.1-lib\lib\bcprov-jdk16-1.46.jar -Durlhttp://ip:port/repository/maven-releases/ -DrepositoryIdsnapshot…

Linux系统编程——文件的打开及创建

打开(open) 使用open函数需要包含以下三个头文件&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> open的函数定义格式 int open(const char *pathname,int flags); int open(const char *pathname,int flags,mode_t mode…

C语言 变量

C 变量 变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有特定的类型&#xff0c;类型决定了变量存储的大小和布局&#xff0c;该范围内的值都可以存储在内存中&#xff0c;运算符可应用于变量上。 变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下…

POI.5.2.4常用操作-数据导入导出常规操作

1、APACHE POI简介 Apache POI 简介是用Java编写的免费开源的跨平台的 Java API&#xff0c;Apache POI提供API给Java程式对Microsoft Office&#xff08;Excel、WORD、PowerPoint、Visio等&#xff09;格式档案读和写的功能。 1.1、其他操作Excel工具 Java Excel是一开放源码…

pda超高频RFID工业手持终端,远距离多标签读取神器

RFID技术的基本原理是通过射频信号进行通信。RFID系统由两部分组成&#xff0c;一是RFID读写器&#xff0c;二是RFID标签。读写器通过发送射频信号&#xff0c;激活标签&#xff0c;并从标签中读取存储的数据。标签可以附着在物品上&#xff0c;并携带物品的相关信息。当物品通…

Codeforces Round 908 (Div. 2)题解

目录 A. Secret Sport 题目分析: B. Two Out of Three 题目分析: C. Anonymous Informant 题目分析: A. Secret Sport 题目分析: A,B一共打n场比赛&#xff0c;输入一个字符串由A和‘B’组成代表A赢或者B赢&#xff08;无平局&#xff09;&#xff0c;因为题目说明这个人…

迅为龙芯3A5000主板,支持PCIE 3.0、USB 3.0和 SATA 3.0显示接口2 路、HDMI 和1路 VGA,可直连显示器

性能强 采用全国产龙芯3A5000处理器&#xff0c;基于龙芯自主指令系统 (LoongArch)的LA464微结构&#xff0c;并进一步提升频率&#xff0c;降低功耗&#xff0c;优化性能。 桥片 桥片采用龙芯 7A2000&#xff0c;支持PCIE 3.0、USB 3.0和 SATA 3.0显示接口2 路、HDMI 和1路 …

9 网关的作用

1、总结&#xff1a; 1.如果离开本局域网&#xff0c;就需要经过网关&#xff0c;网关是路由器的一个网口。 2.路由器是一个三层设备&#xff0c;里面有如何寻找下一跳的规则 3.经过路由器之后 MAC 头要变&#xff0c;如果 IP 不变&#xff0c;相当于不换护照的欧洲旅游&#…

HBase学习笔记(1)—— 知识点总结

目录 HBase概述 HBase 基本架构 HBase安装部署启动 HBase Shell HBase数据读写流程 HBase 优化 HBase概述 HBase是以 hdfs 为数据存储的&#xff0c;一种分布式、非关系型的、可扩展的 NoSQL 数据库 关系型数据库和非关系型数据库的区别&#xff1a; 关系型数据库和非关…

UT代码编译至build文件夹

得克萨斯大学奥斯汀分校代码&#xff1a;代码文件按照网上很多的做法是直接**cmake .****make**则会出现以下的内容&#xff1a;但是这样做未免有些杂乱&#xff0c;会将编译生成的Makefile和其他数据文件全部存放在utaustinvilla3d-master下&#xff0c;比较杂乱。根据我们编译…

Linux--gcc与make

文章目录 gcc/g的使用背景知识gcc与ggcc的编译过程预处理编译汇编链接 函数库自动化构建工具--make三个时间伪目标文件其他表示方法mybin的推导过程 gcc/g的使用 背景知识 GCC是一个开源的编译器套件&#xff0c;支持多种编程语言&#xff0c;并提供了广泛的语言特性和标准库…

C++入门学习(4)引用 (讲解拿指针比较)

上期回顾 在学习完函数重载之后&#xff0c;我们可以使用多个重名函数进行操作&#xff0c;会发现C真的是弥补了好多C语言的不足之处&#xff0c;真的不禁感概一下&#xff0c;时代的进步是需要人去做出改变的&#xff0c;而不是一味的使用啊&#xff01;所以我们今天继续学一下…

674. 最长连续递增序列 718. 最长重复子数组 1143.最长公共子序列 1035.不相交的线

674. 最长连续递增序列 题目&#xff1a; 给定一个未经排序的整数数组nums&#xff0c;找到最长且 连续递增的子序列&#xff0c;并返回该序列的长度。 dp数组含义&#xff1a; dp[i]&#xff1a;以下标i为结尾的连续递增的子序列长度为dp[i]。 递推公式&#xff1a; 怎么…

3D RPG Course | Core 学习日记四:鼠标控制人物移动

前言 前边我们做好了Navgation智能导航地图烘焙&#xff0c;并且设置好了Player的NavMeshAgent&#xff0c;现在我们可以开始实现鼠标控制人物的移动了。除了控制人物移动以外&#xff0c;我们还需要实现鼠标指针的变换。 实现要点 要实现鼠标控制人物移动&#xff0c;点击…

Python 框架学习 Django篇 (九) 产品发布、服务部署

我们前面编写的所有代码都是在windows上面运行的&#xff0c;因为我们还处于开发阶段 当我们完成具体任务开发后&#xff0c;就需要把我们开发的网站服务发布给真正的用户 通常来说我们会选择一台公有云服务器比如阿里云ecs&#xff0c;现在的web服务通常都是基于liunx操作系统…

虹科示波器 | 汽车免拆检测 | 2017款路虎发现车行驶中发动机抖动且加速无力

一、故障现象 一辆2017款路虎发现车&#xff0c;搭载3.0L发动机&#xff0c;累计行驶里程约为3.8万km。车主反映&#xff0c;车辆在行驶过程中突然出现发动机抖动且加速无力的现象&#xff0c;于是请求拖车救援。 二、故障诊断 拖车到店后首先试车&#xff0c;发动机怠速轻微抖…

Unity地面交互效果——5、角色足迹的制作

大家好&#xff0c;我是阿赵。   之前几篇文章&#xff0c;已经介绍了地面交互的轨迹做法。包括了法线、曲面细分还有顶点偏移。Shader方面的内容已经说完了&#xff0c;不过之前都是用一个球来模拟轨迹&#xff0c;这次来介绍一下&#xff0c;怎样和角色动作结合&#xff0c…

深入浅出理解ResNet网络模型+PyTorch实现

温故而知新&#xff0c;可以为师矣&#xff01; 一、参考资料 论文&#xff1a;Identity Mappings in Deep Residual Networks 论文&#xff1a;Deep Residual Learning for Image Recognition ResNet详解PyTorch实现 PyTorch官方实现ResNet 【pytorch】ResNet18、ResNet20、…

文件夹重命名:解决文件夹名称难题,批量将中文翻译成英文

在日常生活和工作中&#xff0c;我们经常需要处理各种各样的文件和文件夹。然而&#xff0c;有时候我们会遇到文件夹名称混乱或者无法识别的问题&#xff0c;这给我们的文件管理和查找带来了很大的不便。为了解决这些问题&#xff0c;我们可以使用文件夹批量重命名技巧&#xf…

strtok函数详解:字符串【分割】的利器

目录 一&#xff0c;strtok函数简介 二&#xff0c;strtok函数的用法 三&#xff0c;strtok函数的注意事项 一&#xff0c;strtok函数简介 strtok函数可以帮助我们将一个字符串按照指定的分隔符进行分割&#xff0c;从而得到我们想要的子字符串。 &#x1f342;函数头文件&am…