0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

聊一聊消息队列技术选型的7种消息场景

jf_ro2CN3Fa 来源:君哥聊技术 2023-12-09 17:50 次阅读

1 普通消息

消息队列最基础的功能就是生产者发送消息、Broker 保存消息,消费者来消费消息,以此实现系统解耦、削峰填谷的作用。

d8f47a96-9640-11ee-8b88-92fbcf53809c.png

普通消息是消息队列必备的消息类型,也是系统使用场景最多的一种消息。

2 顺序消息

顺序消息是指生产者发送消息的顺序和消费者消费消息的顺序是一致的。比如在一个电商场景,同一个用户提交订单、订单支付、订单出库,这三个消息消费者需要按照顺序来进行消费。如下图:

d91a241c-9640-11ee-8b88-92fbcf53809c.png

顺序消息的实现并不容易,原因如下:

生产者集群中,有多个生产者发送消息,网络延迟不一样,很难保证发送到 Broker 的消息落盘顺序是一致的;

如果 Broker 有多个分区或队列,生产者发送的消息会进入多个分区,也无法保证顺序消费;

如果有多个消费者来异步消费同一个分区,很难保证消费顺序跟生产者发送顺序一致。

要保证消息有序,需要满足两个条件:

同一个生产者必须同步发送消息到同一个分区;

一个分区只能给同一个消费者消费。

如下图:

d931cdb0-9640-11ee-8b88-92fbcf53809c.png

上面第二个条件是比较容易实现的,一个分区绑定一个消费者就可以,主要是第一个条件。

在主流消息队列的实现中,Kafka 和 Pulsar 的实现方式类似,生产者给消息赋值一个 key,对 key 做 Hash 运算来指定消息发送到哪一个分区。比如上面电商的例子,对同一个用户的一笔订单,提交订单、订单支付、订单出库这三个消息赋值同一个 key,就可以把这三条消息发送到同一个分区。

对于 RocketMQ,生产者在发送消息的时候,可以通过 MessageQueueSelector 指定把消息投递到那个 MessageQueue,如下图:

d94b1964-9640-11ee-8b88-92fbcf53809c.png

示例代码如下:

publicstaticvoidmain(String[]args)throwsUnsupportedEncodingException{
try{
DefaultMQProducerproducer=newDefaultMQProducer("please_rename_unique_group_name");
producer.start();

String[]tags=newString[]{"TagA","TagB","TagC","TagD","TagE"};
for(inti=0;i< 100; i++) {
   int orderId = i % 10;
   Message msg =
    new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i,
     ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
   SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
    @Override
    public MessageQueue select(Listmqs,Messagemsg,Objectarg){
Integerid=(Integer)arg;
intindex=id%mqs.size();
returnmqs.get(index);
}
},orderId);

System.out.printf("%s%n",sendResult);
}

producer.shutdown();
}catch(MQClientException|RemotingException|MQBrokerException|InterruptedExceptione){
e.printStackTrace();
}
}

RabbitMQ 的实现是 Exchange 根据设置好的 Route Key 将数据路由到不同的 Queue 中。示例代码如下:

@Resource
privateAmqpTemplaterabbitTemplate;

publicvoidsend1(Stringmessage){
rabbitTemplate.convertAndSend("testExchange","testRoutingKey",message);
}

3 延时消息

或者也叫定时消息,是指消息发送后不会立即被消费,而是指定一个时间,到时间后再消费。经典的场景比如电商购物时,30 分钟未支付订单,让订单自动失效。

3.1 RocketMQ 实现

RocketMQ 定义了 18 个延时级别,每个延时级别对应一个延时时间。下面如果延迟级别是 3,则消息会延迟 10s 才会拉取。

//MessageStoreConfig类
privateStringmessageDelayLevel="1s5s10s30s1m2m3m4m5m6m7m8m9m10m20m30m1h2h";

RocketMQ 的延时消息如下图:

d95d1470-9640-11ee-8b88-92fbcf53809c.png

生产者把消费发送到 Broker 后,Broker 首先把消息保存到 SCHEDULE_TOPIC_XXXX 这个 Topic,然后调度任务会判断是否到期,如果到期,会把消息从 SCHEDULE_TOPIC_XXXX 取出投递到原始的 queue,这样消费者就可以消费到了。

RocketMQ 的延时消息只支持最大两个小时的延时,不过 RocketMQ5.0 基于时间轮算法实现了定时消息,解决了这个问题。

3.2 Pulsar 实现

Pulsar 的实现如下图:

d96f90aa-9640-11ee-8b88-92fbcf53809c.png

Pulsar 的延时消息首先会写入一个 Delayed Message Tracker 的数据结构中,Delayed Message Tracker 根据延时时间构建 delayed index 优先级队列。消费者拉取消息时,首先去 Delayed Message Tracker 检查是否有到期的消息。如果有则直接拉取进行消费。

3.3 RabbitMQ 实现

RabbitMQ 的实现方式有两种,一种是投递到普通队列都不消费,等消息过期后被投递到死信队列,消费者消费死信队列。如下图:

d97ff652-9640-11ee-8b88-92fbcf53809c.png

第二种方式是生产者发送消息时,先发送到本地 Mnesia 数据库,消息到期后定时器再将消息投递到 broker。

3.4 Kafka 实现

Kafka 本身并没有延时队列,不过可以通过生产者拦截器来实现消息延时发送,也可以定义延时 Topic,利用类似 RocketMQ 的方案来实现延时消息。

4 事务消息

事务消息是指生产消息和消费消息满足事务的特性。

RabbitMQ 和 Kafka 的事务消息都是只支持生产消息的事务特性,即一批消息要不全部发送成功,要不全部发送失败。

RabbitMQ 通过 Channel 来开启事务消息,代码如下:

ConnectionFactoryfactory=newConnectionFactory();
connection=factory.newConnection();
Channelchannel=connection.createChannel();
//开启事务
channel.txSelect();
channel.basicPublish("directTransactionExchange","transactionRoutingKey",null,message.getBytes("utf-8"));
//提交事务或者channel.txRollback()回滚事务
channel.txCommit();

Kafka 可以给多个生产者设置同一个事务 ID ,从而把多个 Topic 、多个 Partition 放在一个事务中,实现原子性写入。

Pulsar 的事务消息对于事务语义的定义是:允许事件流应用将消费、处理、生产消息整个过程定义为一个原子操作。可见,Pulsar 的事务消息可以覆盖消息流整个过程。

RocketMQ 的事务消息是通过 half 消息来实现的。以电商购物场景来看,账户服务扣减账户金额后,发送消息给 Broker,库存服务来消费这条消息进行扣减库存。如下图:

d9957c3e-9640-11ee-8b88-92fbcf53809c.png

可见,RocketMQ 只能保证生产者发送消息和本地事务的原子性,并不能保证消费消息的原子性。

5 轨迹消息

轨迹消息主要用于跟踪消息的生命周期,当消息丢失时可以很方便地找出原因。

轨迹消息也跟普通消息一样,也需要存储和查询,也会占用消息队列的资源,所以选择轨迹消息要考虑下面几点:

消息生命周期的关键节点一定要记录;

不能影响正常消息的发送和消费性能;

不能影响 Broker 的消息存储性能;

要考虑消息查询维度和性能。

RabbitMQ Broker 实现了轨迹消息的功能,打开 Trace 开关,就可以把轨迹消息发送到 amq.rabbitmq.trace 这个 exchange,但是要考虑轨迹消息会不会给 Broker 造成 压力进而导致消息积压。RabbitMQ 的生产者和消费者都没有实现轨迹消息,需要开发者自己来实现。

RocketMQ 生产者、Broker 和消费者都实现了轨迹消息,不过默认是关闭的,需要手工开启。

使用轨迹消息,需要考虑记录哪些节点、存储介质、性能、查询方式等问题。

6 死信队列

在消息队列中,死信队列主要应对一些异常的情况,如下图:

d9a7ce48-9640-11ee-8b88-92fbcf53809c.png

RocketMQ 实现了消费端的死信队列,当消费者消费失败时,会进行重试,如果重试 16 次还是失败,则这条消息会被发送到死信队列。

RabbitMQ 实现了生产者和 Broker 的死信队列,下面三种情况,消息会被发送到死信队列:

生产者发送消息被拒绝,并且 requeue 参数设置为 false;

Broker 消息过期了;

队列达到最大长度。

RabbitMQ 消息变成死信消息后,会被发送到死信交换机(Dead-Letter-Exchange)。

7 优先级消息

有一些业务场景下,我们需要优先处理一些消息,比如银行里面的金卡客户、银卡客户优先级高于普通客户,他们的业务需要优先处理。如下图:

d9ba8416-9640-11ee-8b88-92fbcf53809c.png

主流消息队列中,RabbitMQ 是支持优先级队列的,代码如下:

ConnectionFactoryfactory=newConnectionFactory();
connection=factory.newConnection();
Channelchannel=connection.createChannel();
Mapargs=newHashMap();
//设置优先级为5
args.put("x-max-priority",5);
channel.queueDeclare("my-priority-queue",true,false,false,args);

8 总结

消息队列技术选型,要考虑的因素很多,本文主要从业务场景来分析需要考虑的因素,同时技术上也需要考虑运维复杂度、业务规模、社区活跃度、学习成本等因素。希望本文对你使用消息队列有所帮助。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Hash算法
    +关注

    关注

    0

    文章

    43

    浏览量

    7362
  • 调度器
    +关注

    关注

    0

    文章

    94

    浏览量

    5161

原文标题:消息队列技术选型的 7 种消息场景

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    为什么正在QQ时,系统消息提示“已被迫下线”?

    ,那么您的密码很可能已经泄露。建议您立即修改密码,如果发现QQ被盗用请点这里查看帮助。还有一种情况可能会导致用户收到“被迫下线”提示,如果您正在使用路由器设备拨号上网,当您重新启动路由器或重新拨号
    发表于 02-05 11:24

    电子工程师之家QQ群150628376,线上线下交朋友,谈工作,...

    电子工程师之家150628376,线上线下交朋友,谈工作,人生。。喜欢的顶起。
    发表于 09-27 16:24

    Linux 下用UDP实现群聊单

    Linux 下用UDP实现群聊单,能够实现单和群聊。程序不长,可以作为学习网络的检测。
    发表于 07-19 23:37

    BIM培训主要讲哪些东西,上海有这个培训的?大家进来...

    有没有了解BIM系列软件的?好像是可以三维数字化,信息化建模的,目前在建筑方面用得比较多,这种新技术有没有可能用在电子设计方面?听位大神说过正在开发电子设计方面的。上海-同建教育,在做这方面的培训,我问了下,他们主要是做建筑
    发表于 09-07 14:24

    下阻抗匹配

    下阻抗匹配,电路加个阻抗匹配网络,搞成和负载样的阻抗,这样岂不是多了个假负载?不是损耗了效率吗?而事实上刚好相反,哪个坛友分析下缘故
    发表于 10-31 17:55

    不吹不黑,今天我们来 Kubernetes 落地的三方式

    的服务治理方式,这类企业发展时间比较久,技术负担比较重,无法立即切换到云原生的服务治理方式,时无法抛弃多年的技术积累,这类用户主要集中在些中型或大型的私有云的 Kubernetes
    发表于 10-12 16:07

    消息队列的应用场景

    、流量削锋  流量削锋也是消息队列中的常用场景般在秒杀或团抢活动中使用广泛!  应用场景:秒杀活动,般会因为流量过大,导致流量暴增,应
    发表于 06-23 10:19

    ARM的八寻址方式

    ;堆栈指针指向下个待压入数据的空位置,称为空堆栈。所有可以组合出四类型的堆栈方式:满递增:堆栈向上增长,堆栈指针指向内含有效数据项的最高地址。指令如LDMFA、STMFA等;空递增:堆栈向上增长
    发表于 08-22 09:00

    Altium中Fill,Polygon Pour,Plane的区别和用法

    Fill会造成短路,为什么还用它呢?来Altium中Fill,Polygon Pour,Plane的区别和用法
    发表于 04-25 06:29

    你知道的和不知道的电流镜

    这期来点轻松的,你知道的和不知道的电流镜。电流源可算是模拟集成电路中最基础的内容,也是有很多花样的基本单元。电流源是笼统的叫法,具体会根据电流的流向,分别叫做电流源(Current
    发表于 06-24 06:56

    stm32的低功耗调试

    前言:物联网的大部分设备都是电池供电的,设备本身低功耗对延长设备使用至关重要,今天就实际调试总结stm32的低功耗调试。1、stm32在运行状态下的功耗上图截图自stm32l15x手册
    发表于 08-11 08:18

    7系列FPGA的供电部分

    前几篇咱们说了FPGA内部逻辑,本篇咱们再聊7系列FPGA的供电部分。首先咱们说spartan7系列,通常咱们需要使用以下电源轨:1,VCCINTFPGA内部核心电压。其不损坏FP
    发表于 11-11 09:27

    下GS的波形

    对于咱们电源工程师来讲,我们很多时候都在看波形,看输入波形,MOS开关波形,电流波形,输出二极管波形,芯片波形,MOS管的GS波形,我们拿开关GS波形为例来下GS的波形。我们测试MOS管GS波形
    发表于 11-16 09:15

    平衡小车代码的实现

    前言今天代码,只有直立功能的代码。代码总体思路给定个目标值,单片机通过IIC和mpu6050通信,得知数据后,根据角度环计算出个P
    发表于 01-14 08:29

    串口环形队列常用的几种方法

    1、串口常用的几种方式查询方式可靠性很高,要考虑下个数据包覆盖上个数据包的问题,小数据量,在10个字节以内,可以这样考虑, 很简单,很方便,很可靠。但是在数据量大的时候,程序阻
    发表于 07-21 15:17