• MetaQ(RocketMQ)


    1 有什么用

    一般常用来进行业务异步解耦、解耦微服务、流量削峰填谷、消息分发、分布式事务的数据一致性等。

    1.1 业务异步解耦 : 在正常业务流程中,比较耗时而且不需要即时返回结果的操作。将这些操作可以做为异步处理,这样可以大大的加快请求的响应时间。

    最常见的场景就是用户注册之后,需要发送注册短信、邮件通知,以告知相关信息。
    正常做法,是要经过三大步处理:用户信息处理、发送邮件、发送短信,等这三步全部都完成之后,才返回前端,告诉你注册成功了。
    使用MQ,只需要在处理完用户信息之后,给MQ发送两个消息即可,邮件服务、短信服务监听MQ的任务消息,根据消息进行发送即可。

    1.2 解耦微服务

    还是用户注册的例子,将用户注册、邮件/短信发送理解为两个独立的微服务,就非常好理解

    1.3 流量削峰填谷

    控制流量,也是MQ比较常用的一个场景,一般在秒杀、搞活动中使用广泛。这个时候一般用户请求量会激增,可能会远超当前系统的最大处理量,如果不做任何处理,系统可能就会宕掉。
    使用MQ,可以将需要处理的消息全部放入其中,系统按照最大处理能力,去获取消息进行消费,这样就可以将一瞬间过来的请求,分散到一段时间内进行处理,避免了系统的崩溃。

    1.4 消息分发

    多个系统对同一个数据感兴趣,只需要监听同一类消息即可。
    例如付款系统,在付款成功之后,正常做法是通知外围系统这个单子付款成功了,或者是外围系统定时来拉取付款结果,使用MQ后,付款系统可以在付款成功之后,将消息放到MQ里面,想知道这个结果的系统订阅这个主题的消息即可,非常方便,也不需要定时去拉取数据了。

    2 是什么

    MQ(Message Queue)消息队列,是基础数据结构中先进先出的一种数据结构。指把要传递的数据(消息)放到队列中,用队列机制来实现消息传递 - 生产者产生消息并把消息放入队列,然后由消费者去处理。消费者可以到指定队列去拉取消息,或者订阅相应的队列,由MQ服务端给其推送消息。

    • 是一个队列模型的消息中间件,具有高可用,高可靠,高实时,分布式的特点
    • Producer,Consumer,队列都可以分布式
    • Producer向一些队列轮流发送消息,队列集合称为Topic,Consumer如果做广播消费,则一个consumer实例消费这个Topic的所有对应的队列,如果做集群消费,则多个Consumer实例平均消费这个Topic对应的队列集合
    • 能够保证严格的消费顺序
    • 提供丰富的消费拉取模式
    • 高效的订阅者对平扩展能力
    • 实时的消息订阅机制
    • 亿级消息堆积能力

    消费关系

    在这里插入图片描述

    经典的Producer,Broker,Consumer消费模型,不同类型的消息以不同的Topic来区分。为了解决性能问题,一个Topic下可以根据需求设置为一个或多个消息队列,Message Queue类似数据库中的分区。Producer按照负载均衡策略将消息发送到Topic不同的消息队列中,Consumer可以从队列中取出消息并发的去消费。

    物理部署结构(高可用)

    在这里插入图片描述

    • NameServer : 用于服务发现,提供服务名称,NameServer是一个无状态节点,可以集群部署,节点之间无任何信息同步
    • Broker : MetaQ的服务器,负责消息中转,主要职责是存储,转发消息。部署相对复杂,Broker分为Master和Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master。Master和Slave之间的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个,每个Broker与Name Server集群中的所有节点建立长连接,定时注册Topic信息到所有的Name Server。
    • Producer : 消息生产者,负责生产消息,一般是业务系统。Producer与Name Server集群中的一个节点(随机选择)建立长连接,定期从Name Server种获取Topic路由信息,并向提供Topic服务的Master建立长连接,并且定时向Master发送心跳。Producer完全无状态,可集群部署。
    • Consumer : 消息消费者,负责消费消息,一般是后台系统负责异步消费。Consumer与Name Server集群中的一个节点(随机选择)建立长连接,定期从Name Server获取Topic路由信息,并且向提供Topic服务的Master,Slave建立长链接,且定时向Master,Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。

    通信协议

    MetaQ通信组件直接使用Netty,在其上做了简单的封装。Netty是一个事件驱动的网络编程框架,它屏蔽了Java底层的复杂细节,具备高性能,高可靠性。
    网络协议
    在这里插入图片描述

    1. length 4 个字节整数,等于 2、3、4 长度总和
    2. header 4 个字节整数,等于 header data 的长度
    3. 使用 json 序列化数据
    4. 应用自定义二进制序列化数据

    数据存储结构

    7-495d-a239-15fc15ac89b&title=& )

    目录结构
    在这里插入图片描述

    MetaQ消息存储是由CommitLog和ConsumeQueue配合完成的,CommitLog真正存储消息,存储在实际CommitLog物理存储中,ConsumeQueue存储消息在CommitLog上的索引,类似于数据库的索引文件,存储的是消息在实际CommitLog物理存储中的偏移地址。每个Topic下的每个Queue都有一个ConsumeQueue文件,文件地址在{root}/consumequeue/{topicName}/{queueId}

    CommitLog以物理文件存储,每个Broker上的CommitLog被该机器上的所有Queue共享(也就是说一个Broker上只有一个CommitLog文件)

    • 所有数据单独存储到一个CommitLog,完全顺序写,随机读
    • 对最终用户展现的队列实际只存储消息在CoomitLog的位置信息,并且串行方式刷盘

    优点

    1. 队列轻量化,单个队列数据量非常少
    2. 对磁盘的访问串行化,避免磁盘竞争,不会因为队列增加导致IO等待时间增多

    缺点

    1. 顺序写,随机读
    2. 读一条消息,会先读ConsumeQueue,再读CommitLog,增加了kaixiao
    3. 要保证CommitLog和ConsumeQueue完全一致,增加l编程的复杂度

    对于缺点的克服

    1. 随机读 : 尽可能让读命中page cache,减少IO读操作,所以内存越大越好。如果系统中堆积的消息过多,**读数据要访问磁盘不会由于随机读导致系统性能急剧下降。**访问page cache时,即使只访问1k的消息,系统也会提前预读出更多的数据,在下次读时就会命中内存。
    2. 随机访问CommitLog磁盘数据,系统IO调度算法设置位NOOP方式,会在一定程度上完全随机,读变成顺序跳跃方式,而顺序跳跃方式读较完全随机读性能高5陪以上
    3. 由于Consume Queue 存储数据极少,而且是顺序读,在page cache预读作用下,Consume Queue的读性能几乎与内存一致,即使堆积情况下。所以可认为Consume Queue完全不会阻碍读性能
    4. CommitLog中存储了所有的元信息,包含消息体,类似于Mysql,Oracle的redolog,所以只要有CommitLog在,Consume Queueu即使数据丢失,仍然可以恢复出来。

    消息持久性刷盘策略

    metaq的所有消息都是持久化的,先写入系统page cache,然后刷盘,可以保证内存和磁盘都有一份数据
    访问时,直接从内存读取。目前,metaq的刷盘策略使用的是异步刷盘等方式
    在这里插入图片描述

    异步刷盘
    broker节点收到消息之后,将消息写入缓存便返回成功。当收到一定数量的消息条数或一定量大小的消息,再批量刷到磁盘
    同步刷盘
    broker节点收到消息,将消息写入缓存,并会当缓存中的数据落到磁盘之后,才会返回成功

    高级特性

    顺序消息
    1 全局顺序

    对于指定的一个Topic,所有的消息严格按照先入先出的(FIFO)的顺序进行发布和消费
    在这里插入图片描述

    2 分区顺序

    对于指定的一个Topic,所有的消息根据sharding key进行区块分区。同一个分区内的消息严格按照FIFO的顺序发布和消费。
    在这里插入图片描述

    将同一个Topic下的多个消息消息队列,进行分区
    顺序消息的缺陷

    • 发送顺序消息无法利用集群的FailOver特性
    • 消费顺序消息的并行依赖于队列数量
    • 队列热点问题,个别队列由于哈嘻不均导致消息过多,消费速度跟不上,产生消息堆积问题
    • 遇到消费失败的消息,无法跳过,当前队列消费暂停
    事务消息

    在这里插入图片描述

    1. 发送方向MQ服务端发送half消息
    2. MQ Server将消息持久化成功之后,向发送方发送ACK消息,确认消息持久化成功
    3. 发送方开始执行本地事物逻辑
    4. 发送方根据本地事物的执行结果向MQ Server提交二次确认(Commit或Rollback),MQ Server收到Commit状态则将半消息标记为可投递,订阅方最终将收到此消息;MQ Server收到Roolback状态则删除半消息,订阅方将不会收到此消息。
    5. 在断网或应用重启的特殊情况下,上述步骤4提交的二次确认并未到达MQ Server,经过固定的时间之后MQ Server将对未提交的消息发起回查
    6. 发送方收到消息回查之后,需要检查对应消息的本地事物的执行结果
    7. 发送方根据本地事物的回查结果进行二次确认,MQ Server仍按照步骤4对半消息进行操作

    事务消息发送对应步骤1,2,3,4 ,事务回查对应步骤5,6,7

    负载均衡

    1 对于生产者的负载均衡
    主要在于生产者发送消息时,会发往broker里面topic分区。多个broker里对同一个topic有多个分区,生产者发送的消息需要确认发往那个分区。此处负载均衡策略是,将所有的broker里面的分区组成一个圈,按从头到位循环遍历选择topic分区发送消息。
    2 对于消费者的负载均衡
    主要是消费者在消费时,会选择consumer group的哪个consumer进行消费
    此处策略是

    • 每个topic分区针对同一个consumer group,只选择一个consumer进行消费
    • 如果consumer group的consumer数量大于topic分区(也就是队列)数量,则多出的consumer不消费
    • 如果同一个consumer group的consumer数量小于topic分区(也就是队列)数量,则有些consumer需要承担更多的消费任务

    3 怎么用

    引用文章 : https://blog.csdn.net/qq_48607414/article/details/125801014

    3.3 最佳实践

    Producer最佳实践

    1. 每个消息在业务层面的唯一标识,要设置keys字段,方便将来定位消息丢失问题。由于是哈希索引,key值尽量保持唯一性,可避免潜在的哈希冲突。
    2. 消息发送成功或失败,要打印日志,务必打印sendResult和key字段
    3. 对于消息不可丢应用,务必要有消息重发机制。例如:消息发送失败,存储到数据库,能有定时程序尝试重发或人工触发重发。
    4. 某些应用如果不关注消息是否是否发送成功,请直接使用sendOneWay方法发送消息

    Consumer最佳实践

    1. 消费过程要做到幂等(即消费端去重)
    2. 尽量使用批量方式消费,可以很大程度上提高消费吞吐量
    3. 优化每条消息的消费过程
  • 相关阅读:
    流媒体技术基础-流媒体服务与框架
    Huggingface遇到 Couldn‘t reach xxx on the Hub (ConnectionError)解决方法
    解决 clickhouse jdbc 偶现 failed to respond 问题
    报道 | 2023-2024年1月国际运筹优化会议汇总
    Java中灵活使用Mockito
    双十一数码好物分享,2022年数码好物选购清单
    深度解读《深度探索C++对象模型》之C++虚函数实现分析(一)
    Linux 系统安装 Redis7 —— 超详细操作演示!
    MG动画完成练习
    vue中自定义指令的使用场景及示例
  • 原文地址:https://blog.csdn.net/weixin_47552725/article/details/126331102