RocketMQ初探

一、MQ的应用场景

  1.1 异步解偶

   在订单系统中同步调用支付系统,库存系统,物流系统。当其中任意一个系统异常,则影响整个业务流程。引入MQ后,订单系统将消息发往MQ,由MQ将消息推送到下游系统。订单系统不需要关心后续的执行情况,整个下单流程已完成。倘下游系统异常,则由MQ负责重试。

  1.2 削峰填谷

  上游系统尽可能多的接收订单,将数据发往MQ,由于整个系统的处理能力有限,MQ中的消息将分批次的交由下游系统处理,虽然整个流程较平时会有延迟,但胜在用户依然能正常下单,而不是返回失败

  1.3 数据分发

  将一份数据分发给多个下游系统

  1.4 异步回调

  1.5其他

    1.5.1 顺序收发:保证消息的先进先出

    1.5.2 分布式事务一致性:MQ事务消息  

二、MQ的优缺点

  优点:解偶、数据分发、削峰

  缺点

    1. 可用性降低(外部依赖越多稳定性越差,如何保证高可用?)

    2. 复杂度提高(需要处理重复消费问题,消息丢失问题,消息传递的顺序性)

    3. 一致性问题

三、消息类型

  3.1 普通消息

    同步发送(发送并等待MQ返回发送结果),异步发送(发送者提供异步回调接口由MQ调用),单向发送(发送者不关心发送结果)  

  3.2 顺序消息

    对于同一个Topic,发布者按照什么顺序发布消息,消费者会按照同样的顺序收到消息。

    顺序消息分为全局顺序消息和分区顺序消息

    全局顺序:同一个Topic,所有消息先进先出

    分区顺序:同一个Topic,根据Sharding Key进行区块分区,同一个分区内(同一个消息队列)消息先进先出

  3.3 广播消息

    集群消费模式:每条消息只被集群下的某台机器消费。MQ重试时不保证路由到同一台机器

    广播消费模式:每条消息被集群下所有消费者处理。

    1. 广播模式不支持顺序消息

    2. 广播模式不支持重置消费位点

    3. 每条消息需要被相同订阅

  3.4 延迟消息

    订单需要30分钟内支付完成,超过30分钟订单状态设置为未支付。适用于生产和消费之间有时间窗口的要求。实现思路是将延迟消息通过临时存储进行暂存,到期后投递到目标Topic中。

    对临时存储的要求:

    1.高性能,写入性能要高,关系型数据库通常不满足

    2.高可靠,消息不能丢失,需要持久化

    3. 支持排序,不同延迟的消息发送时间不同

    4. 支持长时间保存,延迟时间可能几个月。

  3.5 RocketMQ的延迟消息

    延迟消息发送后,存放在一个特殊的topic下,不同的延迟级别有不同的队列序号,队列数是由配置的延迟等级决定的,一个延迟等级一个队列。在延迟时间到达后,由定时线程读取转化成普通的消息存放在真实的Topic下。RokcetMQ的延迟时间是固定等级的,如果想要自定义需要提前修改配置。

  3.6 批量消息

    发送者一次发送多条消息,批消息的topic是相同的,但是每条消息的msgId是不一样的。一批消息大小不能超过1M

  3.7 过滤消息

    过滤方式:    

    3.7.1 TAG模式过滤

    发送消息时为每条消息设置TAG标签,同一TOPIC下根据TAG分类。 

    3.7.2 SQL表达式过滤

    发送端通过message.putUserProperty()方法设置属性  

    消费端订阅消息时通过运行SQL过滤表达式进行条件匹配:consumer.subscribe("topic",MessageSelector.bySql("age between 10 and 20"));

  3.8 事务消息

    3.8.1 概念介绍

    1. 事务消息:提供类似Open X/XA的分布式事务功能

    2. 半事务消息:不能立即投送的消息,需要发送者的二次确认

    3. 消息回查:对半事务消息,每段时间向发送者确认,发送者提供的回调接口返回三种状态:提交,回滚和未知

    3.8.2 注意事项

    1. 事务消息不支持延迟和批量

    2. 消息回查默认15次,超过则丢弃,并打印错误日志

    3. 超时时间在Broker配置文件的transactionTimeout参数设置,可以通过用户属性CHECK_IMMUNITY_TIME_IN_SECONDS改变限制

    4. 事务消息可能不止一次被检查/消费,需要做好幂等

    5. 事务消息的生产者ID不能与其他类型消息的生产者ID共享

    3.8.3 流程

    1. 发送端发送半消息。sendMessageInTransaction方法

      1. 将消息打上事务消息的标记,服务端以区分

      2. 发送半事务消息(DefaultMQProducerImpl#send方法,请求头SysFlag属性设置成TRANSACTION_PREPARED_TYPE)

    2. 服务端接收半消息。TransactionalMessageService#prepareMessage方法

      1. 将半消息的真实topic,queueId放进消息体自身的map里进行缓存(TransactionalMessageBridge#parseHalfMessageInner方法)

      2. 将半消息的topic 设置为“RMQ_SYS_TRANS_HALF_TOPIC”,queueId设置为0(TransactionalMessageBridge#parseHalfMessageInner方法)

      3. 将半消息写入commitLog进行持久化(MessageStore#putMessage方法)

    3. 发送端执行本地事务并通知执行结果。sendMessageInTransaction方法

      1. 发送成功,执行本地事务executerLocalTransaction方法,方法返回枚举LocalTransactionState(提交,回滚,未知)

      2. 执行endTransaction方法:根据本地事务执行情况,通知服务端提交还是回滚

    4. 服务端接收发送端执行结果。EndTransactionProcessor#processRequest方法

      1. 根据通知请求体中消息的文件偏移量,取出半消息,还原消息的topic和queueId

      2. 若发送端通知提交,重新存储到commitLog(sendFinalMessage方法)

      3. 不管是提交还是回滚,都需要删除半消息(deletePrepareMessage方法)。

       实际是新创建一个消息,消息体为半消息的队列偏移量,并存放到RMQ_SYS_TRANS_OP_HALF_TOPIC的队列中,这个topic下的半消息都是已经处理过的。

    5. 服务端定时回查。TransactionalMessageCheckService#onWaitEnd方法

      1. 获取RMQ_SYS_TRANS_HALF_TOPIC的半消息队列,循环遍历队列,逐个处理

      2. 做下过滤,已经处理过的不再处理(在RMQ_SYS_TRANS_OP_HALF_TOPIC的队列中)

      3. 做下过滤,最大回查次数不超过15次(默认,具体看broker的transactionCheckMax配置)和最大存储时间不超过3天

      4. 做下过滤,半消息刚放进队列,还不需要执行回查(有个超时时间,默认6秒,具体看broker的transactionTimeOut配置,用户通过赋值CHECK_IMMUNITY_TIME_IN_SECONDS属性设置)

      5. 进行回查,resolveHalfMsg(),内部有线程池提交回查任务

    6. 发送端处理回查请求。DefaultMQProducerImpl#checkTransactionState方法

      1. 根据TransactionListener#checkLocalTransaction方法获取对应事务Id的状态

      2. 根据状态通知服务端提交还是回滚

    源码分析:https://segmentfault.com/a/1190000019755235?utm_source=tag-newest,https://blog.csdn.net/hosaos/article/details/90240260,https://www.sohu.com/a/287495304_612370

      

 


posted @ 2021-03-25 13:53  walker993  阅读(87)  评论(0编辑  收藏  举报