Distributed Systems and message delivery ¶

The message is a very interesting concept, which is issued by the communication unit a discrete source, or is sent to a group of recipients, whether single service or a distributed system have concepts of the message, but the two systems method channel or different channels for transmitting messages; monomer message service often can communicate by way between the IO, the communication process, method call, the remote call in a distributed system requires a network, using the TCP or UDP protocol, etc. transmission.

However, the network is the least controllable in the computer world, if we call request other services through a network interface, for various reasons might not have delivered the message to the service goals for the current service we can not control the transmission network in many cases it is difficult to control the quality of network communications, which is why "network is stable and reliable," one of the common fallacy distributed system.

  • Unreliable communication channels is an important reason for building large-scale distributed systems are very complex and difficult.

Network requests

Each node as a communication channel between distributed systems, network actually very unreliable means of communication, if we want to ensure consistency node status, the complexity of this means of communication allows us to deal with during the very time cross-service call many boundary conditions, realization of the principle of distributed systems · article distributed transactions simply introduced before, network traffic may contain, successes, failures, and three cases timeout.

Every time they are actually a network request delivery of information, due to the current node can not know the other node information, only to learn the results of the information delivered by responding to network requests.

Success and failure

Although the network is relatively unstable, but most of the time when we have some of the information transmitted over the network, whether it is the result of the return of success or failure, in fact, can get the result of the determination:

Each response will need to determine the correct processing requests in a round trip and is called a node, the flow can be either an intermediate proxy packet loss, not because of errors led to the target node can not issue a response only when these two meet If the condition that we can get the results to determine the response. For nodes, the success or failure of this request returns are better deal, because as long as the result of the determination, network requests such inter-process communication or communication with a more reliable way of these calls in dealing with do not have much when the difference, but other problems arise in the course of communication is more tricky.

Timeout

In a distributed system, not any network requests can be determined in response to the request if the network or node error occurred packet loss round-trip and in the process of being called a node processing, the requesting node may never get this request response times.

Each node after request, all of the routes and how the request is processed ignorant, so nodes need to set an appropriate time-out, if the request is not returned within the stipulated time, it will assume the current request has timed out , which is the network request failed.

Network request timed out is one of the root causes of distributed systems difficult to deal with this problem occurs when a node does not know whether the current target node receives a request for idempotent network requests Fortunately, once the request may change the target state of the node is very tricky, because we can not determine the last time the network request in which step failed, if a failure occurs in the process of return is responding, if the request will be retried a number of problems, may trigger bank two transfers, which we can not accept in any case; All in all, an unstable network communication forces us to deal with complex problems arise because of a timeout, which is also in the development of distributed systems have to consider.

Message delivery semantics

Using the network to communicate really is not a reliable way in a distributed system, the sender of a message can only know the current control node, there is no way to ensure the reliability of the transmission channel, network timeouts this common communication error greatly increased a distributed communication system complexity, we can encapsulate the basic transmission capacity provided by the network, to ensure the reliability of data communication.

网络请求由于超时的问题,消息的发送者只能通过重试的方式对消息进行重发,但是这就可能会导致消息的重复发送与处理,然而如果超时后不重新发送消息也可能导致消息的丢失,所以如何在不可靠的通信方式中,保证消息不重不漏是非常关键的。

我们一般都会认为,消息的投递语义有三种,分别是最多一次(At-Most Once)、最少一次(At-Least Once)以及正好一次(Exactly Once),我们分别会介绍这三种消息投递语义究竟是如何工作的。

最多一次

最多一次其实非常容易保证的,UDP 这种传输层的协议其实保证的就是最多一次消息投递,消息的发送者只会尝试发送该消息一次,并不会关心该消息是否得到了远程节点的响应。

无论该请求是否发送给了接受者,发送者都不会重新发送这条消息;这其实就是最最基本的消息投递语义,然而消息可能由于网络或者节点的故障出现丢失。

最少一次

为了解决最多一次时的消息丢失问题,消息的发送者需要在网络出现超时重新发送相同的消息,也就是引入超时重试的机制,在发送者发出消息会监听消息的响应,如果超过了一定时间也没有得到响应就会重新发送该消息,直到得到确定的响应结果。

对于最少一次的投递语义,我们不仅需要引入超时重试机制,还需要关心每一次请求的响应,只有这样才能确保消息不会丢失,但是却可能会造成消息的重复,这就是最少一次在解决消息丢失后引入的新问题。

正好一次

虽然最少一次解决了最多一次的消息丢失问题,但是由于重试却带来了另一个问题 - 消息重复,也就是接受者可能会多次收到同一条消息;从理论上来说,在分布式系统中想要解决消息重复的问题是不可能的,很多消息服务提供了正好一次的 QoS 其实是在接收端进行了去重。

消息去重需要生产者生产消息时加入去重的 key,消费者可以通过唯一的 key 来判断当前消息是否是重复消息,从消息发送者的角度来看,实现正好一次的投递是不可能的,但是从整体来看,我们可以通过唯一 key 或者重入幂等的方式对消息进行『去重』。

消息的重复是不可能避免的,除非我们允许消息的丢失,然而相比于丢失消息,重复发送消息其实是一种更能让人接受的处理方式,因为一旦消息丢失就无法找回,但是消息重复却可以通过其他方法来避免副作用。

投递顺序

由于一些网络的问题,消息在投递时可能会出现顺序不一致性的情况,在网络条件非常不稳定时,我们就可能会遇到接收方处理消息的顺序和生产者投递的不一致;想要满足绝对的顺序投递,其实在生产者和消费者的单线程运行时是相对比较好解决的,但是在市面上比较主流的消息队列中,都不会对消息的顺序进行保证,在这种大前提下,消费者就需要对顺序不一致的消息进行处理,常见的两种方式就是使用序列号或者状态机。

序列号

使用序列号保证投递顺序的方式其实与 TCP 协议中使用的 SEQ 非常相似,因为网络并不能保证所有数据包传输的顺序并且每个栈帧的传输大小有限,所以 TCP 协议在发送数据包时加入 SEQ,接受方可以通过 SEQ 将多个数据包拼接起来并交由上层协议进行处理。

在投递消息时加入序列号其实与 TCP 中的序列号非常类似,我们需要在数据之外增加消息的序列号,对于消费者就可以根据每一条消息附带的序列号选择如何处理顺序不一致的消息,对于不同的业务来说,常见的处理方式就是用阻塞的方式保证序列号的递增或者忽略部分『过期』的消息。

状态机

使用序列号确实能够保证消息状态的一致,但是却需要在消息投递时额外增加字段,这样消费者才能在投递出现问题时进行处理,除了这种方式之外,我们也可以通过状态机的方式保证数据的一致性,每一个资源都有相应的状态迁移事件,这些事件其实就是一个个消息(或操作),它们能够修改资源的状态:

在状态机中我们可以规定,状态的迁移方向,所有资源的状态只能按照我们规定好的线路进行改变,在这时只要对生产者投递的消息状态做一定的约束,例如:资源一旦 completed 就不会变成 failed,因为这两个状态都是业务逻辑中定义的最终状态,所以处于最终状态的资源都不会继续接受其他的消息。

假设我们有如下的两条消息 active 和 complete,它们分别会改变当前资源的状态,如果一个处于 pending 状态的资源先收到了 active 再收到 complete,那么状态就会从 pending 迁移到 active 再到 completed;但是如果资源先收到 complete 后收到 active,那么当前资源的状态会直接从 pending 跳跃到 completed,对于另一条消息就会直接忽略;从总体来看,虽然消息投递的顺序是乱序的,但是资源最终还是通过状态机达到了我们想要的正确状态,不会出现不一致的问题。

协议

消息投递其实有非常多相关的应用,最常见的组件就是消息队列了,作为一种在各个 Web 项目中常用的组件,它提供了很多能力,包括消息的持久存储、不同的投递语义以及复杂的路由规则等等,能够显著地增加系统的可用性、起到比较比明显的削峰效果。

在这里将介绍几种比较常见的消息队列协议,我们将简单说明各个协议的作用以及它们的实现原理和关键特性,也会简单提及一些遵循这些协议实现的消息队列中间件。

AMQP 协议

AMQP 协议的全称是 Advanced Message Queuing Protocol,它是一个用于面向消息中间件的开放标准,协议中定义了队列、路由、可用性以及安全性等方面的内容。

该协议目前能够为通用的消息队列架构提供一系列的标准,将发布订阅、队列、事务以及流数据等功能抽象成了用于解决消息投递以及相关问题的标准,StormMQ、RabbitMQ 都是 AMQP 协议的一个实现。

在所有实现 AMQP 协议的消息中间中,RabbitMQ 其实是最出名的一个实现,在分布式系统中,它经常用于存储和转发消息,当生产者短时间内创建了大量的消息,就会通过消息中间件对消息转储,消费者会按照当前的资源对消息进行消费。

RabbitMQ 在消息投递的过程中保证存储在 RabbitMQ 中的全部消息不会丢失、推送者和订阅者需要通过信号的方式确认消息的投递,它支持最多一次和最少一次的投递语义,当我们选择最少一次时,需要幂等或者重入机制保证消息重复不会出现问题。

MQTT 协议

另一个用于处理发布订阅功能的常见协议就是 MQTT 了,它建立在 TCP/IP 协议之上,能够在硬件性能底下或者网络状态糟糕的情况下完成发布与订阅的功能;与 AMQP 不同,MQTT 协议支持三种不同的服务质量级别(QoS),也就是投递语义,最多一次、最少一次和正好一次。

从理论上来看,在分布式系统中实现正好一次的投递语义是不可能的,这里实现的正好一次其实是协议层做了重试和去重机制,消费者在处理 MQTT 消息时就不需要关系消息是否重复这种问题了。

总结

在分布式系统中想要保证消息的送达确实是一件比较复杂的事情,通信方式的不确定使得我们需要处理很多问题,我们既需要在网络错误或者超时时进行重试,还需要对一些请求支持重入和幂等,保证不会出现一致性的错误;这其实都是因为在分布式系统中,正好一次的消息投递语义是不存在的,消息要么可能会丢失,要么就可能会重复。

Guess you like

Origin www.cnblogs.com/burningmyself/p/12112854.html