持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情
什么是消息系统
消息系统是用来处理生产者和消费者之间的通信。
直接的消息系统
比如
- UDP组播
- 无代理的消息库,比如ZeroMQ
- RPC或HTTP风格的服务
传统消息系统:AMQP和JMS风格
传统的消息代理,体现在AMQP和JMS这样的标准,像RabbitMQ,ActiveMQ等都是这样的实现。
多个消费者
在可能有多个消费者的时候,有以下处理方式:
- 负载均衡 每一条消息都只被传递给一个消费者,并且采用负载均衡的方式来分享。
客户端必须在处理完消息之后显示的告诉代理,以便代理可以将其从队列中移除。 但可以看出,如果使用负载均衡,则很难保证消息的消费是有顺序的。
如果消息彼此独立,那就没有关系。
- 扇出式 则每一条消息都被传递给所有消费者。
分区日志
基于日志的消息存储
使用AMQP和JMS风格的消息代理时,如果确认后则从消息代理删除这个消息,无法再次接收消息,因此不能再次运行同一个消费者并期望得到相同的结果。如果将一个消费者加入到消息系统,通常只会接收在注册后发送的消息,任何之前的消息已经消失,无法恢复了。而文件和数据库,则可以随时添加新的客户端,并且可以读取以前任意的写入数据。
日志消息代理背后的思想:将数据库的持久存储和消息传递的低延迟功能功能相结合。
日志是磁盘上一个支持追加时修改记录的序列。生产者将消息追加到日志末尾来发送消息,消费者一次读取日志来消费消息。
为了突破单个磁盘的带宽吞吐的限制,可以对日志进行分区。每个分区称为一个单独的日志。可以将一个主题定义为一组分区,它们都携带相同类型的消息。 在每个分区中,带来为每个消息分配一个单调递增的序列号或偏移量,在分区内这个值上单调增长的。分区之间则没有顺序保证。
Kafka等都是基于这种方式工作的。尽管这些消息代理将所有消息写入磁盘,但通过在多台机器分区,能实现每秒数百万条的吞吐量,并且通过复制消息实现了容错性。
消费者偏移量
在一个分区中,所有偏移量小于消费者当前偏移量的消息已经被处理。因此,代理不需要追逐每条消息的确认,需要定期记录消费者的偏移量。减少的记录开销可以提高基于日志的系统的吞吐量。 并且重新启动的消费者将从此偏移量处开始继续执行。
磁盘空间的使用
如果持续不断的追加日志,磁盘空间最终将被耗尽。
为了回收磁盘空间,日志实际上是被分割成段,并且不时的将酒段删除或归档保存。这就意味着,如果一个消费者慢到难以跟上消息生产的速度,并且远远落后以至于消费者偏移量指向了已经被删除的片段,那么消费者会错过一些消息。实际上,日志实现了一个有限大小的缓冲区,当缓冲区变满时,旧的消息会被丢弃,该缓冲区也被称为循环缓冲区。由于该缓冲区在磁盘上,因此它可以非常大。
让我们来做一个粗略的估计。假设一个磁盘容量为6TB,顺序写入吞吐量为150MB/s。如果以最快速度写入,则大约11小时就可以填满磁盘。实际的部署中,很少达到磁盘的满写带宽,所以日志通常可以保存几天甚至几周的消息缓冲区。
不管保留多长时间的消息,因为每个消息都被写入磁盘,因此日志的吞吐量基本保持不变。这种行为与将消息默认保存在内存中,仅当队列变得过大时才将它们写入磁盘的消息系统相比,差异明显:当队列很短的时候这些系统时很快的,当开始写入磁盘时,会变得很慢,因此吞吐量取决于保留的历史记录数量。
当消费者跟不上生产者时
消费者无法跟上生产者发送消息的速度时三种选择:丢弃、缓冲或者背压。 在这个分类方法中,基于日志的方法是一种缓冲形式,它具有较大但固定大小的缓冲区(首可用磁盘空间的限制)。 如果消费者落后太多,并且开始丢失消息,也只有该消费者受到影响,不会中断其他消费者的服务。可以通过实验性的方式使用生产日志进行开放、测试,而不必担心中断服务。当消费者关闭或奔溃时,它会停止消耗资源,唯一留下的就是消费者偏移量。 这和传统的消息代理不同,在传统的代理,需要小心的删除消费者已经关闭的任何队列。
重新处理消息
使用AMQP和JMS风格的消息代理时,由于会导致消息在代理上被删除,因此处理和确认操作可视为带有一定的破坏性。而基于日志的消息代理中,使用消息像是从文件读取,这是只读操作,并不会更改日志。
重新处理的副作用时消费者偏移量前移了,但是偏移量在消费者的控制之下,在必要时可以轻松的对其进行操作。
和传统消息系统的对比
多个消费者独立的读取日志而不受影响,读取日志不会导致日志删除。基于日志的方法就是传统消息队列的扇出式,另外为了在一组消费者之间实现负载均衡,可以将不同的分区分配给不同的消费者。但是为了保证分区消费的有序性,须以单线程的方式读取分区中的消息,所以消费一个分区的节点数最多等于分区数,如果一个消息处理缓慢,则会影响后续的消息处理。
因此,在消息处理的代价很高,希望并行处理,而且消息排序又不那么重要的情况下,AMQP和JMS风格的消息代理更可取。另一方面,在消息吞吐量高的情况下,每个消息处理速度快,消息顺序又很重要的情况下,基于日志的方法工作的很好。