消息中间件以及技术选型

一 什么是消息中间件?为什么使用消息中间件或者说消息中间件的使用场景?

1.1 什么是消息中间件

消息中间件:就是可以介于两个系统之间,用于存储、交换、路由消息的一个平台或者系统,

1.2 为什么使用消息中间件

1.2.1 异步处理

当程序运行的时候,主线程或者说发起线程需要干一些事情,但是并不需要同步等待这件事情的结果,我们可以创建一个异步线程来干这件事情,也可以通过消息中间件来实现,将这个事件或者消息发送到消息中间件,程序继续往下走,消息中间件的消费者消费消息,实现具体的业务逻辑,从而达到异步处理的效果。比如:用户注册的时候需要给用户以邮件或者短信的形式发送注册成功的消息;或者给新注册的用户赠送优惠券,这样的异步比同步更加节约时间

1.2.2 应用解耦

当一个系统需要产生的核心消息需要被多个系统消费的时候,原始的做法是可能直接调用其他系统的服务接口,比如商品系统更新数据需要被索引和更新购物车商品数据和缓存数据,另外将日志写入ElasticSearch,也满足这种场景,如图示:

更新商品数据需要重新进行索引,并且需要更新购物车系统中商品数据。如果商品系统自己调用搜索系统的索引服务和购物车系统数据更新服务,那么当搜索系统不可用或者购物车系统不可用的时候,怎么办呢? 如图示:

这时候,难道要让商品系统回滚数据?亦或是让购物车系统丢失这条更新? 不可能商品系统一直重试吧。所以都不是靠谱的方案,如果可以将这个数据保存起来,等到购物车系统可用的时候在消费,虽然不是实时,但是数据不会丢失,也算是实现了最终一致性。那如何存储这条消息呢,那么消息中间件就可以干这件事,商品系统将数据发送到消息中间件,然后搜索系统和购物车系统作为消费者从消息中间件消费数据,当购物车系统不可用的时候,恢复的时候数据还在消息中间件,然后可以继续消费。所以这就实现了商品系统和搜索系统以及购物车系统的解耦,如图示:

1.2.3 流量消峰

当我们程序运行的时候,有时候会遇到高峰期,比如晚上7-9点,或者是一些网站做活动的时候,流量很大。这时候按照平常的部署可能无法处理这么大的流量,这时候可能会想到增加机器来解决,这也是可以的,但是这样的高峰期只有1-2小时,其余时候根本就没这么大的流量,所以会导致机器浪费。所以我们这时候也可以利用消息中间件来抗高并发,后面的系统从中间件慢慢的消费,这样也不用增加机器,也不用担心系统被压垮。但是这种场景有限,必须是用户的请求可以抛弃的场景,否则消息中间件满了,怎么办?比如抢购和秒杀可以使用这种,但是促销场景的流量增多不适合,因为促销要保证的是需要保证用户的请求,不能直接丢弃。

二 消息中间件需要解决哪些问题?

2.1 发布订阅

发布订阅是消息中间件基础功能。所以RabbitMQ、RocketMQ、Kafka都是支持的。

2.2 消息持久化

为了防止消息丢失,必须保证消息可以持久化,这也是消息中间件必须的基础功能。所以RabbitMQ、RocketMQ、Kafka都是支持的。

2.3 消息堆积

消息堆积也是消息中间件的基本功能,因为消费者数量不足或者因为某时间段洪峰到来,当消费速率低于生产速率的时候就会造成消息大量堆积在服务器,如果不支持消息堆积,那么这条数据可能就会丢失。所以RabbitMQ、RocketMQ、Kafka都是支持的。

2.4 消息确认

消息确认也是消息中间件的基本功能。ActiveMQ、RabbitMQ、RocketMQ、Kafka都支持消息确认。其中RabbitMQ的消息确认分为发送方确认机制和接收方确认机制,当接收方autoAck设置为false,需要消费者显示确认。Kafka确认机制主要分为3个级别,ack=0: 只要发送出去了就确认了;ack=1:只要Leader副本收到就确认,ack=-1: 必须Leader副本和所有Follower副本接收到才确认。

2.5 消息重试

当消费者消费消息,没有返回响应的时候,服务器端不知道消费者到底是否消费成功,所以超时没有响应则重新将消息发送给消费者。目前ActiveMQ、RabbitMQ和RocketMQ支持消息重试,Kafka不支持。

2.6 死信队列

当消息被消费者拒绝、消息超时未被消费或者消息队列满了,这个消息可能会变成死信,可能会放到死信队列。如果直接丢弃肯定是不好的方案,最好的方案是保存一个特殊的队列里,后面还可以继续消费。那么这个队列就是死信队列(Dead-Letter Queue),死信队列的消息就是死信消息(Dead-Letter Message)。当然也只有ActiveMQ、RabbitMQ、RocketMQ支持死信队列,Kafka不支持。

2.7 定时(延时)消息

有时候,生产者将消息发送到Broker, 但是不希望消息立刻被消费,而是希望在指定的时间或者过一段时间再被消费。ActiveMQ、RabbitMQ、RocketMQ支持延时消息,Kafka不支持延时消息。

2.8 消息回溯

指的是消息中间件具备重复消费的能力,ActiveMQ和RabbitMQ不支持消息回溯功能,Kafka支持基于指定分区的消费offset位置回溯,RocketMQ支持定点时间回溯。

2.9 顺序消息

有时候需要保证消息按照生产者发送的顺序,在broker顺序存储和消费者顺序消费,比如使用canal进行mysql binlog增量同步,如果先增加,后修改然后删除,但是消费则消费的顺序是先删除、在修改肯定就有问题了。目前ActiveMQ、RocketMQ和Kafka支持局部有序,RocketMQ只保证同一个主题的相同队列的消息是有序的,Kafka也只是能保证同一个分区下的消息是有序的,除非RocketMQ只有一个队列,Kafka只有一个分区才是全局有序。

2.11 消息优先级

有时候需要支持消息优先级,即优先级高的消息先被消费。ActiveMQ和RabbitMQ支持消息优先级,但是RocketMQ和Kafka不支持优先级,只能通过其他变通的方式实现。

RabbitMQ实现了消息优先级:

#1 设置队列的x-max-priority=n参数: 表示该队列有n个优先级,从高到低是n,n-1......0

#2 设置消息的Priority参数: 设置具体的优先级

2.12 消息过滤

消息过滤有两种,一种是服务器端过滤,一种是客户端过滤。严格的讲,每一个消息中间件在客户端都可以按照业务需求自行过滤,但是服务器端过滤只有RocketMQ实现了,RocketMQ可以通过TAG实现消息过滤。RabbitMQ、Kafka服务器端没有实现消息过滤的功能,只能在客户端实现。

2.13 事务

消息有时候需要事务功能。RabbitMQ实现了生产者发送时候的事务功能;RocketMQ支持分布式事务的功能;Kafka在0.11版本也实现了事务的功能。RocketMQ和Kafka分布式事务都属于2阶段提交。

RocketMQ:

第一阶段:提交半消息,只是在Broker中CommitLog中存在,不会被消费

第二阶段:根据提交半消息的状态,执行本地事务,根据本地事务结果

2.14 幂等性

当生产者发送消息的时候,我们需要考虑幂等性。比如生产者发送消息到Broker,Broker写入磁盘,然后向生产者确认。如果写入成功,按时确认的时候因为网络或者Broker挂了,那么此时生产者接收不到确认,可能会发起重试,则又发送一次,如果没有幂等机制,那势必日志中又要多添加一条数据,这样就会被重复消费。

RabbitMQ、RocketMQ都是无法保证幂等性,只能在消费端去重。Kafka0.11版本之前也是无法保证幂等的,但是0.11版本通过ProducerId和Sequence Number可以实现幂等性。

ProducerId: 在每个新的Producer初始化时,会被分配一个唯一的ProducerID,这个ProducerID对客户端使用者是不可见的。

SequenceNumber:生产者每次发送的RecordBatch都有一个单调递增的sequence number,Broker上每个分区也会维护<pid,seq>的映射,并且每次提交都会更新lastSeq(上一次最后的序列号)。当RecordBatch到来的时候,broker会先检查seq是不是大于lastSeq,如果大于则保存数据;否则说明已经保存了,无需再次保存。

三 常见的消息中间件有哪些,具备哪些特点, 如何进行消息中间件选型?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值