# Kafka 系列 - 消息队列

消息队列是分布式系统中的重要组件,也是 Java 开发中常用的技术点之一。

使用消息队列可以解决模块间的解耦、流量削峰、异步消息,提高系统的可用性、稳定性以及性能。

# 消息队列的优点与应用场景

# 解耦

在大型项目中,如果使用传统的通过方法进行模块间的相互调用的形式,无法保证系统稳定性。

假设任务告警模块需要在触发告警时将告警信息发送给任务管理模块,那么传统模式中,任务告警模块在调用任务管理模块时,将会报错导至告警消息丢失。如下图:

任务告警-任务管理

如果引入消息队列的话,则有告警消息时,任务告警模块将告警信息生产到消息队列。任务管理模块监听消息队列,消息队列有消息时则消费。

这样即使任务管理模块异常了,告警消息也会存在于消息队列中,等待任务管理模块正常后进行消费。两个模块之间通过消息队列进行解耦,达到高可用的目的。如下图:

任务告警-任务管理-消息队列

# 流量削峰

在传统架构中,如果有大量的数据库读写请求涌入,一瞬间压在数据库上,数据库有可能吃不消。 如果采用消息队列,系统A按照自身的处理速度去消息队列中去消息,然后做数据库请求,则可以有效的减轻数据库的压力。 流量削峰也可以应用在秒杀活动中,秒杀活动一般会有大量请求涌入。通过设置消息队列的长度来控制活动的人数,也可以减轻秒杀应用的压力。 将先到的请求消息存到消息队列,队列满了之后,则将后到来请求抛弃或者跳转到错误页面,秒杀业务再根据业务逻辑处理消息队列中的消息。如下图:

流量削峰-秒杀业务

# 异步消息

从前面两个优点也可以看出,发送到消息队列上的时候,生产者可以不用等待消息的处理结果。

一些非必要的业务逻辑参杂在主流程中,会消耗整个流程太多的时间。可以将非必要的业务逻辑消息发送到消息队列,通过异步的方式处理。

比如下单支付之后会有一条短信通知,这个短信通知的部分,就可以通过异步的方式处理,减少支付的响应时间。

异步-支付流程

# 消息队列的缺点

消息队列也不全是好处,引入消息队列还会给系统带来一系列的问题。

# 增加系统的复杂度

在串行处理的代码逻辑里,按照处理流程一步一步写好就可以了。但是引入消息队列,需要考虑的东西更多,比如如何保证消息不被重复消费、如何保证消息可靠传输以及一致性问题。

# 系统可用性降低

本来只要系统的各个模块运行正常,就不会有什么问题。但是在引入消息中间件后,还需要维护多一个组件,如果组件挂了,那么整个系统也就瘫痪了。因此,可用性降低了。

# 研发成本增加

引入消息队列后,首先需要对选择的消息队列有深入的了解,以及相应的技术选型。消息队列本身也比较复杂,需要根据具体的应用场景进行判断。

# 消息队列的三大重要特性

# 幂等性

在百度百科中,关于幂等的描述如下:

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

生产者在进行重试的时候,有可能会重复写入消息,需要通过消息队列的幂等性来避免这种情况发生。

可以通过为每条消息设置唯一 ID,消费是判断该 ID 是否已经消费过,消费过则丢弃。

# 顺序性

当两个业务之间存在先后关系时,需要保证操作的顺序性,这样才不会发生业务处理错误。比如有下单和支付两个操作,需要先下单才有支付的操作。

如果有下单消息和支付消息两条队列,它们各自的顺序混乱的,并不是按照消息生产的顺序入队,那么就可能会在支付的时候找不到下单消息,从而导致支付失败。

对于消息中间件的消息顺序性问题,一般通用的处理方案是保证局部的消息有序。

# 可靠性

消息队列的可靠性指的是消息丢失,如何防止消息丢失,消息丢失后如何处理?

对于消息可靠性来说,如果要保证 100% 不丢消息,需要从生产者、服务器、消费者三个地方入手,做好参数配置和异常处理,尽最大努力保证消息不丢失。

上次更新: 2023/10/15