前言
生产者上一篇大家都读过了吧?没读过赶紧先去上一篇读
上一篇我们说了生产者了,既然有生产者总得有消费者吧,就和养鸡一个道理,鸡下了蛋你总得有人吃吧
你要反驳我说我可以不吃啊,我可以孵小鸡啊!!
那我也得反驳你两句,首先,孵小鸡也是属于消费啊,只是和当成鸡蛋吃掉是不同的消费方式罢了,因为,最后蛋没了!
还有,如果全天下的鸡蛋都孵小鸡的话,那岂不是灾难!?
哎呦喂,扯远了,回归主题,继续学习,学习才是我的最爱
嗯,没错,这是分割线,严肃脸更换ing
消息队列RocketMQ版是阿里云基于Apache RocketMQ构建的低延迟、高并发、高可用、高可靠的分布式消息中间件。
消息队列RocketMQ版既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。
下面列举了一些特点
消息查询:消息队列RocketMQ版提供了三种消息查询的方式,分别是按Message ID、Message Key以及Topic查询
查询消息轨迹:通过消息轨迹,能清晰定位消息从生产者发出,经由消息队列RocketMQ版服务端,投递给消息消费者的完整链路,方便定位排查问题
集群消费和广播消费:当使用集群消费模式时,消息队列RocketMQ版认为任意一条消息只需要被消费者集群内的任意一个消费者处理即可;
当使用广播消费模式时,消息队列RocketMQ版会将每条消息推送给消费者集群内所有注册过的消费者,保证消息至少被每台机器消费一次
重置消费位点:根据时间或位点重置消费进度,允许用户进行消息回溯或者丢弃堆积消息
死信队列:将无法正常消费的消息储存到特殊的死信队列供后续处理
全球信息路由:用于全球不同地域之间的消息同步,保证地域之间的数据一致性
下面啊,我们来一起冲浪🏄🏄🏄
消费者Consumer
消费者Consumer,顾名思义,就是负责消费消息的,这肯定也是和业务息息相关的了,因为涉及到消息的处理逻辑,上一篇我们说了生产者这个客户端,通过分析一些问题来更深入的了解生产者
这篇呢,我也想通过一些常见的问题来给大家分析,把关键点以及难点都搞明白了,剩下的就很容易理解了,也就容易把各个知识点串联起来了
消息过滤和消息查找
消费者Consumer,顾名思义,就是负责消费消息的,这肯定也是和业务息息相关的了,因为涉及到消息的处理逻辑
消息过滤,就是对生产者生产的消息按照自己想要的方式进行过滤
不对啊,给我解释解释,既然都不要消费了,当初为什么发这个消息啊,你生产者不生产不发这个消息不行吗,也能减少网络传输
好了,客官,满足您的需要,一个消费者可能要消费多个Topic的消息,但是这个Topic的消息我又不想全要,我只想要消费其中的一部分,这时过滤就派上用场了,当然也可以把这些消息放在同一个Topic中再传输一份,但是这样不就很浪费资源了吗?
说的也对,但是我有钱,我服务器有的是,资源也有的是
那没得聊了,有钱任性
身边认识这样的人的话,麻烦介绍我下,谢谢~
消息过滤分为Tag过滤和SQL过滤两种,Tag过滤应该大家比较熟悉了,就是之前文章说过的二级主题,每个消息必须设置Topic,除了设置Topic之外还可以设置Tag,这个就可以作为二级过滤了
还有一种就是SQL过滤,默认情况不支持,若想使用需要在conf文件夹下修改broker.conf,添加配置 enablePropertyFilter=true,然后启动broker是使该配置文件生效
消息查找呢?
不对啊,按照之前的逻辑不应该都是broker直接推给消费者的吗?那为什么还需要查找消息,消费者获取消息有两种模式push和pull,下面也会说这两种模式,其实底层呢,都是通过pull这一种方式实现的,push其实就是消费者轮询去pull达到的一种效果
消费者在broker拉取消息的时候,就是一个查找的过程,通过消息的消费位移offset去broker中查找消息,broker中存储消息的是commitLog文件。
在此之外呢,还有一个IndexFile文件,这个文件的作用是支持broker还支持通过MsgID或者MessageKey来查询消息;
使用ID查询的时候,因为ID就是用broker和offset生成的,所以很容易就能找到commitLog文件来读取消息;使用MessageKey来查询消息,MessageStore通过构建一个index来提高读取速度
消息队列负载和重新分配机制
队列负载,一个道理就是,薅羊毛你不能搁着一个羊薅啊
在集群消费模式下,每条消息只需要投递到订阅这个topic的Consumer Group下的一个实例即可。RocketMQ采用主动拉取的方式拉取并消费消息,在拉取的时候需要明确指定拉取哪一条message queue。
而每当实例的数量有变更,都会触发一次所有实例的负载均衡,这时候会按照queue的数量和实例的数量平均分配queue给每个实例。
消费模式和拉取模式
消费模式
对于消费者来说,消费模式可以分为两类
集群消费:当使用集群消费模式时,消息队列RocketMQ版认为任意一条消息只需要被集群内的任意一个消费者处理即可。
广播消费:当使用广播消费模式时,消息队列RocketMQ版会将每条消息推送给集群内所有注册过的消费者,保证消息至少被每个消费者消费一次。
集群消费模式
适用场景:适用于消费端集群化部署,每条消息只需要被处理一次的场景。
此外,由于消费进度在服务端维护,可靠性更高。具体消费示例如下图所示。
注意事项:
1、集群消费模式下,每一条消息都只会被分发到一台机器上处理。如果需要被集群下的每一台机器都处理,请使用广播模式。
2、集群消费模式下,不保证每一次失败重投的消息路由到同一台机器上。
广播消费模式
适用场景:适用于消费端集群化部署,每条消息需要被集群下的每个消费者处理的场景
具体消费示例如下图所示。
注意事项
1、广播消费模式下不支持顺序消息,不支持重置消费位点。
2、广播模式下,消息队列RocketMQ版保证每条消息至少被每台客户端消费一次,但是并不会重投消费失败的消息,因此业务方需要关注消费失败的情况。
3、广播模式下,客户端每一次重启都会从最新消息消费。客户端在被停止期间发送至服务端的消息将会被自动跳过,请谨慎选择。
4、广播模式下,每条消息都会被大量的客户端重复处理,因此推荐尽可能使用集群模式。
拉取模式
消息队列RocketMQ版支持以下两种消息获取方式,这个很容易理解了,一个推一个拉,就像蹦极一样,一个是自己跳,一个是被别人推:
Push:消息由消息队列RocketMQ版推送至Consumer。Push方式下,消息队列RocketMQ版还支持批量消费功能,可以将批量消息统一推送至Consumer进行消费。
Pull:消息由Consumer主动从消息队列RocketMQ版拉取。
对于任何一款消息中间件而言,消费者客户端一般有两种方式从消息中间件获取消息并消费。
严格意义上来讲,RocketMQ并没有实现push模式,而是对拉模式进行一层包装,名字虽然是 push 开头,实际在实现时,使用 pull 方式实现。
通过 pull 不断不断不断轮询 Broker 获取消息。当不存在新消息时,Broker 会挂起请求,直到有新消息产生,取消挂起,返回新消息。这样,基本和 Broker 主动 push 做到接近的实时性(当然,还是有相应的实时性损失)。原理类似长轮询( Long-Polling )
push模式
由消息中间件(MQ消息服务器代理)主动地将消息推送给消费者;采用Push方式,可以尽可能实时地将消息发送给消费者进行消费。
但是,在消费者的处理消息的能力较弱的时候(比如,消费者端的业务系统处理一条消息的流程比较复杂,其中的调用链路比较多导致消费时间比较久。
概括起来地说就是“慢消费问题”),而MQ不断地向消费者Push消息,消费者端的缓冲区可能会溢出,导致异常
pull模式
由消费者客户端主动向消息中间件(MQ消息服务器代理)拉取消息;采用Pull方式,如何设置Pull消息的频率需要重点去考虑。
举个例子来说,可能1分钟内连续来了1000条消息,然后2小时内没有新消息产生(概括起来说就是“消息延迟与忙等待”)。
如果每次Pull的时间间隔比较久,会增加消息的延迟,即消息到达消费者的时间加长,MQ中消息的堆积量变大;若每次Pull的时间间隔较短,但是在一段时间内MQ中并没有任何消息可以消费,那么会产生很多无效的Pull请求的RPC开销,影响MQ整体的网络性能
消费的其它问题
消费启动点
消费启动点,为什么要有这个呢?
如果机房网络不好或者由于物理因素导致消费者突然宕机,重启之后就需要重新寻找新的消费启动点来进行消费,所以,要有一个消费启动点的标记
可是每次让消费者从上次宕机的位置继续消费不就好了吗?
为什么还要有这个呢,换一种业务场景考虑,如果这个消息并不是很重要,消费者已经宕机半个月了才重启,半个月可能会有很多消息并且很消耗带宽,此时其实就没必要把之前的消息全部都重新消费一遍了,所以就可以把消费启动点调整到最近的情况
当然这也仅仅是用于一些消息并不是十分重要的场景,要是用于金融行业,每个消息都十分重要,这是就需要考虑多种情况,来设置多重保障,保证消息不丢失
消费轨迹:定制化消费轨迹
消息轨迹是指一条消息从生产者发送到消息队列RocketMQ版服务端,再到消费者消费处理,整个过程中的各个相关节点的时间、状态等数据汇聚而成的完整链路信息。
该轨迹可作为生产环境中排查问题强有力的数据支持。
消费轨迹数据
消息队列RocketMQ版系统中,一条消息的完整链路包含生产者、服务端、消费者三个角色,每个角色处理消息的过程中都会在轨迹链路中增加相关的信息,将这些信息汇聚即可获取任意消息当前的状态。
使用场景
在生产环境的消息收发不符合预期时,可以通过Message ID、Message Key或Topic的时间范围查询相关的消息轨迹,找到消息的实际收发状态,帮助诊断问题。
顺序消费
顺序消息(FIFO消息)是消息队列RocketMQ版提供的一种严格按照顺序来发布和消费的消息。
顺序发布和顺序消费是指对于指定的一个Topic,生产者按照一定的先后顺序发布消息;消费者按照既定的先后顺序订阅消息,即先发布的消息一定会先被客户端接收到。
顺序消息分为全局有序和部分有序,这个是由生产者控制的其实
哦,对了,我上一篇写的就是生产者,而且里面有顺序消费的问题,这里就不啰嗦了
虽然顺序这块在消费者这边没啥问题,但是生产者的发送会引发一个问题,就是重复发送消息的问题
重复消费
顺序消息(FIFO消息)是消息队列RocketMQ版提供的一种严格按照顺序来发布和消费的消息。顺序发布和顺序消费是指对于指定的一个Topic,生产者按照一定的先后顺序发布消息;消费者按照既定的先后顺序订阅消息,即先发布的消息一定会先被客户端接收到。
其实顺序消费,主要的控制逻辑还是在生产者
重复消费,问题根源就是生产者发送了重复消息,那我们先来思考下这个问题能否避免
假设我们发消息就只管发,不管消息的消费已经消费结果等等,那这样发往broker的消息就不会重复了,但是实际上这种情况我们是不允许的,这样消息就完全不可靠了,我们要能做到的保证就是至少发送一次
比如消息发送到broker之后需要等待broker的而相应,就可能存在broker已经写入但是由于网络原因没收到回应,然后生产者又发送了一次,这时候就重复了
此时,消费者就可能拿到重复的消息了,因此很多业务是无法避免这一问题的,所以我们就需要换一个角度来解决
消息幂等性
顺序消息(FIFO消息)是消息队列RocketMQ版提供的一种严格按照顺序来发布和消费的消息。
幂等其实属于数学上的概念,很好理解,其实就是理解成我们用同样的参数多次调用同一个API产生的结果和只调用一次产生的结果是相同的
生活中其实也有不少幂等性的例子,比如我们看见的一些网站的点赞系统,点了赞就没法取消了,也没法二次点赞了,这就是幂等性的例子;
还有就是程序员常见的SQL语句,一句很常见的更新字段语句,多次执行同一SQL,执行结果却是一样的,这也是幂等性
因此需要改造业务处理逻辑,就保证幂等性即可,也不会影响最终的结果
至于如何保证幂等性,就是根据业务场景来定了,其实很多时候的场景的幂等性就可以通过数据库的唯一键来保证
总结
消息队列RocketMQ版的消费者和生产者客户端对象是线程安全的,可以在多个线程之间共享使用。我们可以在服务器上(或者多台服务器)部署多个生产者和消费者实例,也可以在同一个生产者或消费者实例里采用多线程发送或接收消息,从而提高消息发送或接收TPS。
消费者属于RocketMQ的客户端之一,也是我们平时开发最常见的,问题其实还是挺多的,简单汇总一下上面说的
1、消息过滤和消息查找(Tag过滤和SQL过滤)
2、消息队列负载和重新分配机制(负载均衡)
3、消费模式(集群和广播)和拉取模式(push和pull)
4、消费启动点、消费轨迹、顺序消费、重复消费、消息幂等性
结束语
感谢大家能够做我最初的读者和传播者,请大家相信,只要你给我一
份爱,我终究会还你们一页情的。
欢迎大家关注我的公众号【左耳君】,探索技术,分享生活
哦对了,后续所有的文章都会更新到这里
https://github.com/DayuMM2021/Java