消息队列概述
- 在分布式系统和微服务架构中,消息队列(Message Queue)是一个核心组件,用于在不同的应用程序或服务之间异步传递消息
- 在 Go 语言中,有多种实现消息队列的方式,包括使用开源的消息队列服务(如 RabbitMQ、Kafka、RocketMQ)和 基于 Go 语言的消息队列库
消息队列的基本概念
- 消息队列允许生产者(Producer)将消息发送到队列中,而消费者(Consumer)则可以从队列中获取消息进行处理
- 消息队列具有以下特性:
- 异步处理:生产者和消费者无需实时连接,允许异步地处理和分发消息。
- 解耦:通过消息队列,服务之间可以解耦,降低系统间的耦合度。
- 流量削峰:在高峰时段,消息队列可以缓存消息,避免系统过载。
- 可靠性:消息队列通常具有持久化机制,确保消息不会丢失。
常用消息队列的对比
1 )RocketMQ、 kafka、 RabbitMQ
RocketMQ | kafka | RabbitMQ | |
时效性 | ms级别 | 在ms级以内 | 微妙级 |
单机吞吐量 | 10万级 | 10万级 | 万级别,比前面2个小一个量级 |
几百,几千的级别 | 几百,几千的级别 | ||
可用性 | 非常高 分布式架构(大多数保证) | 非常高 分布式架构(大多数保证) | 高 (主从架构实现高可用) |
消息可考证 | 待优化后保证 | 待优化后保证 | |
支持 | 较完善,分布式,扩展好 | 实时计算,日志采集 | erlang并发强 性能好 低延时 |
- 选择:kafka 和 rocketmq 都是 java 生态,rabbitmq 是 erlang 生态,所以 后者被排除,遇到问题,怕很难解决
- kafka 偏 日志 和 实时计算,所以现在 还剩 rocketmq
- 选择合适自己的消息队列,在生产环境,独立部署一套
- 可以先在一台单独的机器上测试
- 现在选择并部署,在生产环境压一下,整个全链路压测多少时间,这样就可以推断出在每一个节点需要多少资源
- 做活动的时候,按照基础评估,增加主从集群配置
- MQ的缺点
- 稳定性:宕机,业务就挂了,要保证高可用
- 复杂性:原来一个服务器内搞定的事情,现在要异步调用,消息丢失怎么办?消息顺序如何保证?重复消费怎么办?
- 一致性: 分布式系统中,多个系统,如果一个系统挂了,如何保证消息处理的一致性,这里涉及到分布式事务,后续继续深入学习
- 所以,MQ 是需要做集群的,如果单点挂了,就挂了…
2 )使用 Go 语言的消息队列库
- NSQ
- NSQ 是一个基于 Go 语言的实时分布式消息队列。它设计用于大规模分布式系统,如实时分析、日志聚合和流数据传递
- NSQ 提供了简单的 API 和丰富的功能,如发布/订阅、持久化、分布式和可扩展
- NATS
- NATS 是一个高性能、轻量级的云原生消息传递系统。
- 它使用发布/订阅模型,支持多种编程语言和平台,包括 Go。NATS 特别适用于微服务架构中的事件驱动通信。
- Go Channel
- 虽然 Go Channel 不是一个完整的消息队列服务,但它为 Go 语言中的并发编程提供了强大的支持
- 在简单的场景下,可以使用 Go Channel 来实现消息队列的功能
- 然而,对于复杂的分布式系统,使用专门的消息队列服务可能更为合适
3 ) 如何选择消息队列
- 在选择消息队列时,需要考虑以下因素:
- 性能:确保消息队列能够满足系统的性能需求。
- 可靠性:确保消息队列的可靠性,以避免数据丢失。
- 可伸缩性:随着业务的发展,系统可能需要处理更多的消息。因此,消息队列需要具有良好的可伸缩性。
- 易用性:选择易于使用和集成的消息队列服务或库。
- 成本:考虑消息队列的部署和维护成本。
RocketMQ的选择与使用
- 文档:https://rocketmq.apache.org
- 消息模型(Message Model)
- RocketMQ主要由Producer、 Broker、Consumer三部分组成,其中Producer负责生产消息,Consumer负责消费消息,Broker负责存储消息
- Broker 在实际部署过程中对应一台服务器,每个Broker可以存储多个Topic的消息,每个Topic的消息也可以分片存储于不同的Broker
- Message Queue用于存储消息的物理地址,每个Topic中的消息地址存储于多个Message Queue中
- ConsumerGroup 由多个Consumer实例构成
1 ) 消息生产者(Producer)
- 负责生产消息,一般由业务系统负责生产消息
- 一个消息生产者会把业务应用系统里产生的消息发送到broker服务器
- RocketMQ提供多种发送方式,同步发送、异步发送、顺序发送、单向发送
- 同步和异步方式均需要Broker返回确认信息,单向发送不需要
2 ) 消息消费者(Consumer)
- 负责消费消息,一般是后台系统负责异步消费。一个消息消费者会从Broker服务器拉取消息、并将其提供给应用程序
- 从用户应用的角度而言提供了两种消费形式:拉取式消费、推动式消费
3 ) 主题(Topic)
- 表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocketMQ进行消息订阅的基本单位
4 ) 代理服务器(Broker Server)
- 消息中转角色,负责存储消息、转发消息
- 代理服务器在RocketMQ系统中负责接收从生产者发送来的消息并存储、同时为消费者的拉取请求作准备
- 代理服务器也存储消息相关的元数据,包括消费者组、消费进度偏移和主题和队列消息等
- 可以理解为 仓库
5 ) 名字服务(Name Server)
- 名称服务充当路由消息的提供者
- 生产者或消费者能够通过名字服务查找各主题相应的BrokerIP列表
- 多个Namesrv实例组成集群,但相互独立,没有信息交换
6 ) 拉取式消费(Pull Consumer)
- Consumer消费的一种类型,应用通常主动调用Consumer的拉消息方法从Broker服务器拉消息、主动权由应用控制
- 一旦获取了批量消息,应用就会启动消费过程
7 ) 推动式消费(Push Consumer)
- Consumer消费的一种类型,该模式下Broker收到数据后会主动推送给消费端,该消费模式一般实时性较高
8 ) 生产者组(Producer Group)
- 同一类Producer的集合,这类Producer发送同一类消息且发送逻辑一致
- 如果发送的是事务消息且原始生产者在发送之后崩溃,则Broker服务器会联系同一生产者组的其他生产者实例以提交或回溯消费
9 ) 消费者组(Consumer Group)
- 同一类Consumer的集合,这类Consumer通常消费同一类消息且消费逻辑一致
- 消费者组使得在消息消费方面,实现负载均衡和容错的目标变得非常容易
- 要注意的是,消费者组的消费者实例必须订阅完全相同的Topic
- RocketMQ支持两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)
10 ) 集群消费(Clustering)
- 集群消费模式下,相同Consumer Group的每个Consumer实例平均分摊消息
11 ) 广播消费(Broadcasting)
- 广播消费模式下,相同Consumer Group的每个Consumer实例都接收全量的消息
12 ) 普通顺序消息(Normal Ordered Message)
- 普通顺序消费模式下,消费者通过同一个消息队列(Topic分区,称作Message Queue)收到的消息是有顺序的
- 不同消息队列收到的消息则可能是无顺序的
14 ) 严格顺序消息(Strictly Ordered Message)
- 严格顺序消息模式下,消费者收到的所有消息均是有顺序的
15 ) 消息(Message)
- 消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题
- RocketMQ中每个消息拥有唯一的MessagelD,且可以携带具有业务标识的Key
- 系统提供了通过Message ID和Key查询消息的功能
16 ) 标签(Tag)
- 为消息设置的标志,用于同一主题下区分不同类型的消息
- 来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签
- 标签能够有效地保持代码的清晰度和连贯性,并优化RocketMQ提供的查询系统
- 消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性
消息队列类型
1 )按照发送方式分
-
同步:发送并阻塞
-
异步:指定回调函数,发送后立即返回即可,不阻塞,在回调中起一个线程执行
-
单向:发送后不返回,只管发,性能高,不可靠,发消息级别在ms级别,大多场景不适用,视频通话可用,但为何用mq呢, 这里不适用视频通话
-
主要关注:同步和异步
2 )按消息类型分
- 普通消息:日常最多,生产者发送,消费者消费,无需保证消息顺序
- 顺序消息:全局顺序(先进先出,开销大)和分区顺序(开销较小,无需严格先进先出)
- 延迟消息:订单超时,库存归还,防止友商下单不支付
- 事务消息:分布式和微服务相关
MQ 使用场景
- 场景:订单新建完成之后,给用户发送一条短信,(下订单成功通知),还要给用户增加积分
- 单体服务,发送完短信,就发送积分增加信息
- 短信是第三方服务,不稳定,体现在第三方服务可能会崩溃,短信发送频率要符合短信平台的规定(限制)
- 短信欠费被停,这样短信发不出去,积分是没法增加
- 1 ) 解决方案
- 异步发送短信,中间加一层 redis, 但要保证 redis 不能挂
- 这也会造成一些问题
- 2 )解决方案升级:
- 把 中间的 redis 替换成 MQ
- mq 是队列,先进先出, 谁先来,消费谁
- 这样,积分服务无需关心订单服务,只需要关心 MQ 中的订阅
- 销峰:订单量大了,直接把消息发送到 MQ 里就算成功,后续订阅MQ的系统可以根据自身情况慢慢消化消费