二、基于异步消息模式的通信
1 消息传递
消息传递两种架构:基于消息代理(服务通信的基础设施服务)的架构、基于无代理的架构。
基于无代理的消息传递架构:服务直接通信
基于消息代理的消息传递架构:服务通过消息通道通信。发送方(应用程序或服务)将消息写入通道,接收方(应用程序或服务)从通道读取消息。
1.1 关于消息
1.1.1 概念
消息组成:头部+主体
消息头部内容:
1、标题是名称和值对的组合,描述正在发送的数据的元数据。
2、发件人或消息传递基础上设施生成的唯一消息ID
3、可选的返回地址(该地址指定发送回复的消息通道)
消息主体:以文本或二进制格式发送的数据。
1.1.2 消息类型
·文档:仅包含数据的通用消息。接收者决定如何解释它。对命令式消息的回复是文档消息的一种使用场景;
·命令:一条等同于RPC请求的消息。它指定要调用的操作及其参数;
·事件:表示发送方这一端发生了重要的事件。事件通常是领域事件,表示领域对象的状态更改。
1.2 关于通道
消息通道类型:点对点、发布-订阅
点对点通道向正在从通道读取的一个消费者传递消息,服务使用点对点通道来实现一对一交互方式。例如,命令式消息通常通过点对点通道发送。
发布-订阅通道将一条消息发送给所有订阅的接收方。服务使用发布-订阅通道来实现一对多交互方式。例如,事件式消息通常通过发布-订阅通道发送。
2 使用消息机制实现交互方式
2.1 请求/响应&异步请求&响应
通过在请求消息中包含回复通道和消息标识符来实现异步请求/响应。接收方处理消息,并将回复发送到指定的回复通道。
由于客户端和服务使用消息机制通信,因此交互本质上是异步的。
2.2 实现单向通知
客户端将消息(通常是命令式消息)发送到服务所拥有的点对点通道。服务订阅该通道并处理该消息,但是服务不会发回回复。
2.3 实现发布/订阅
客户端将消息发布到由多个接收方读取的发布/订阅通道,服务使用发布/订阅来发布领域事件,领域事件代表领域对象的更改。发布领域事件的服务拥有自己的发布/订阅通道,通道的名称派生自领域类。对特定领域对象的事件感兴趣的服务只需订阅相应的通道。
2.4 实现发布/异步响应
结合了发布/订阅和请求/响应
客户端发布一条消息,在消息的头部中指定回复通道,这个通道同时也是一个发布-订阅通道。
消费者将包含相关性ID的回复消息写入回复通道。客户端通过使用相关性ID收集响应,将回复消息与请求匹配。
服务的异步API包含供客户端调用的操作和由服务对外发布的事件。
服务的异步API由消息通道、命令、回复、事件消息类型组成。
3 使用消息代理
优点:
1、发送发不需要知道接收方的网络位置
2、消息代理缓冲消息,直到接收方能够处理它们。
消息代理 | 点对点通道 | 发布-订阅通道 |
JMS | 队列 | 主题 |
Apache Kafka | 主题 | 主题 |
基于AMOP的代理,如RabbitMQ | 交换+队列 | 组播式交换和每客户端队列 |
AWS Kinesis | 流 | 流 |
AWS SQS | 队列 | / |
4 基于消息的架构会遇到的设计难题
4.1 处理并发和消息顺序
问题:如何在保留消息顺序的同时,横向扩展多个接收方的实例。
现代消息代理(Kafka和Kinesis)使用的常见解决方案:分片(分区)通道。
1、分片通道由两个或多个分片组成,每个分片的行为类似于一个通道;
2、发送方在消息头部指定分片键,通常是任意字符串或字节序列。消息代理使用分片键将消息分配给特定的分片(例如,可以通过计算分片键的散列来选择分片)
3、消息代理将接收方的多个实例组合在一起,并将它们视为相同的逻辑接收方。(例如,Kafka使用术语“消费者组”)。消息代理将每个分片分配给单个接收器。它在接收方启动和关闭时重新分配分片。
4.2 处理重复消息
大多数消息代理承诺至少成功传递一次消息。
解决方法:
1、编写幂等消息处理程序
应用程序的幂等性:即使这个应用被相同输入参数多次重复调用,也不会产生额外的效果。
2、跟踪消息并丢弃重复项
消息接收方使用message ID跟踪它已处理的消息并丢弃任何重复项(专表或应用程序表记录)
笔记来自:《微服务架构设计模式》一书,作者 [美] 克里斯·理查森 著,喻勇译 第三章