MQ消息队列

1、消息队列应用场景

消息队列,指保存消息的一个容器,本质是个队列。

  • 异步处理,主要目的是减少请求响应时间;
  • 应用解耦,使用消息队列后,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系;
  • 流量削峰,秒杀活动中,系统峰值流量往往集中于一小段时间,消息队列作为缓冲,可以削弱峰值流量;
  • 日志处理,解决大量日志创数问题;

2、MQ整体架构

 

消息生产者Producer:负责产生和发送消息到Broker;
消息消费者Consumer:负责从Broker中获取消息,并进行相应处理;
消息处理中心Broker:负责消息存储、确认、重试等,一般包含多个queue;

3、设计Broker主要考虑

1)消息的转储:在更合适的时间点投递,或者通过一系列手段辅助消息最终能送达消费机。

2)规范一种范式和通用的模式,以满足解耦、最终一致性、错峰等需求。

3)其实简单理解就是一个消息转发器,把一次RPC做成两次RPC。发送者把消息投递到broker,broker再将消息转发一手到接收端。

总结起来就是两次RPC加一次转储,如果要做消费确认,则是三次RPC。

4、两种模型

1)点对点模型

包含三个角色:

  • 消息队列(Queue)
  • 发送者(Sender)
  • 接收者(Receiver)

每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,可以放在 内存 中,也可以 持久化,直到他们被消费或超时。

特点:

  • 每个消息只有一个消费者(即一旦被消费,消息就不再在消息队列中);
  • 发送者和接收者之间在时间上没有依赖性;
  • 接收者在成功接收消息之后需向队列应答成功;

2)发布者订阅消息模型

模型包含三个角色:

  • 主题(Topic)
  • 发布者(Publisher)
  • 订阅者(Subscriber)

多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

特点:

  • 每个消息可以有多个消费者:和点对点方式不同,发布消息可以被所有订阅者消费;
  • 发布者和订阅者之间有时间上的依赖性;
  • 针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息;
  • 为了消费消息,订阅者必须保持运行的状态;

5、RocketMQ原理

1)基础概念

  • Producer: 消息生产者,负责产生消息,一般由业务系统负责产生消息;
  • Producer Group:消息生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者;
  • Consumer:消息消费者,负责消费消息,一般是后台系统负责异步消费;
  • Consumer Group:消费者组,和生产者类似,消费同一类消息的多个 Consumer 实例组成一个消费者组;
  • Topic:主题,用于将消息按主题做划分,Producer将消息发往指定的Topic,Consumer订阅该Topic就可以收到这条消息;
  • Message:消息,每个message必须指定一个topic,Message 还有一个可选的 Tag 设置,以便消费端可以基于 Tag 进行过滤消息;
  • Tag:标签,子主题(二级分类)对topic的进一步细化,用于区分同一个主题下的不同业务的消息;
  • Broker:Broker是RocketMQ的核心模块,负责接收并存储消息,同时提供Push/Pull接口来将消息发送给Consumer。Broker同时提供消息查询的功能,可以通过MessageID和MessageKey来查询消息。Borker会将自己的Topic配置信息实时同步到NameServer;
  • Queue:Topic和Queue是1对多的关系,一个Topic下可以包含多个Queue,主要用于负载均衡,Queue数量设置建议不要比消费者数少。发送消息时,用户只指定Topic,Producer会根据Topic的路由信息选择具体发到哪个Queue上。Consumer订阅消息时,会根据负载均衡策略决定订阅哪些Queue的消息;
  • Offset:RocketMQ在存储消息时会为每个Topic下的每个Queue生成一个消息的索引文件,每个Queue都对应一个Offset记录当前Queue中消息条数;
  • NameServer:NameServer可以看作是RocketMQ的注册中心,它管理两部分数据:集群的Topic-Queue的路由配置和Broker的实时配置信息。其它模块通过NameServer提供的接口获取最新的Topic配置和路由信息;各 NameServer 之间不会互相通信, 各 NameServer 都有完整的路由信息,即无状态;
    •   Producer/Consumer :通过查询接口获取Topic对应的Broker的地址信息和Topic-Queue的路由配置;
    •   Broker : 注册配置信息到NameServer, 实时更新Topic信息到NameServer;

2)Client的使用

 1 from rocketmq.client import Message, Producer, PushConsumer, PullConsumer
 2 
 3 # Message
 4 msg = Message(self.topic)
 5 msg.set_tags(tag)
 6 msg.set_keys(key)
 7 msg.set_body(json.dumps(self.msg))
 8 
 9 # Producer
10 producer = Producer(self.group_id)
11 producer.set_namesrv_addr(self.namesrv_addr)
12 producer.set_max_message_size(1310720)
13 producer.start()
14 ret = producer.send_sync(msg=msg)                    # 同步发送消息
15 producer.shutdown()
16 
17 # PushConsumer
18 consumer = PushConsumer(listener_name)
19 consumer.subscribe(self.topic, listener_class)    # 订阅topic
20 consumer.set_namesrv_addr(self.namesrv_addr)
21 logger.info('[ListenRocketMq]',
22       u'{} {} {}'.format(self.namesrv_addr, self.topic, 'Monitor MQ heartbeat.'))
23 consumer.start()
24 while True:
25     time.sleep(random.randint(2000, 3000))
26 consumer.shutdown()

PushConsumer VS PullConsumer

PushConsumer,推,Broker主动向Consumer推消息,应用通常向对象注册一个Listener接口,一旦接收到消息,Consumer对象立刻回调Listener接口方法。Push方式里,consumer把轮询过程封装了,并注册MessageListener监听器,取到消息后,唤醒MessageListener的consumeMessage()来消费,对用户而言,感觉消息是被推送过来的。

PullConsumer,拉,Consumer主动的从Broker拉取消息,主动权由应用控制,可以实现批量的消费消息。Pull方式里,取消息的过程需要用户自己写,首先通过打算消费的Topic拿到MessageQueue的集合,遍历MessageQueue集合,然后针对每个MessageQueue批量取消息,一次取完后,记录该队列下一次要取的开始offset,直到取完了,再换另一个MessageQueue。

3)消费模式

  • 广播模式

一条消息被多个Consumer消费,即使这些Consumer属于同一个Consumer Group,消息也会被Consumer Group中的每一个Consumer都消费一次;

1 # 设置广播模式       
2 consumer.setMessageModel(MessageModel.BROADCASTING);
  • 集群模式

一个Consumer Group中的所有Consumer平均分摊消费消息(组内负载均衡);

1 # 设置集群模式,也就是负载均衡模式
2 consumer.setMessageModel(MessageModel.CLUSTERING);

4)Broker的存储结构

Commit log:消息存储文件,混合型存储,即记录所有的topic信息,RocketMQ 会对commit log文件进行分割(默认大小1GB),新文件以消息最后一条消息的偏移量命名;

Consumer queue:消息消费队列(也是个文件),记录每个消费者组消费topic最后的偏移量,消费者是先从 Consume queue 得到消息真实的物理地址,然后再去 Commit log 获取消息;

IndexFile:索引文件,是额外提供查找消息的手段,通过 Key 或者时间区间来查询对应的消息;

整体流程:

Producer 使用轮询的方式分别向每个 Queue 中发送消息。

Consumer 启动时会在 Topic,Consumer group 维度发生负载均衡,为每个客户端分配需要处理的 Queue。负载均衡过程中每个客户端都获取到全部的的 ConsumerID 和所有 Queue 并进行排序,每个客户端使用相同负责均衡算法,例如平均分配的算法,这样每个客户端都会计算出自己需要消费那些 Queue,每当 Consumer 增加或减少就会触发负载均衡,所以我们可以通过 RocketMQ 负载均衡机制实现动态扩容,提升客户端收发消息能力。客户端负责均衡为客户端分配好 Queue 后,客户端会不断向 Broker 拉取消息,在客户端进行消费。

 

posted @ 2023-07-10 17:45  最咸的鱼  阅读(292)  评论(0编辑  收藏  举报