RabbitMQ延时消息实现方案(岳小均)
rabbitmq的延迟消息队列实现
rabbitmq的延迟消息队列实现第⼀部分:延迟消息的实现原理和知识点使⽤RabbitMQ来实现延迟任务必须先了解RabbitMQ的两个概念:消息的TTL和死信Exchange,通过这两者的组合来实现上述需求。
消息的TTL(Time To Live)消息的TTL就是消息的存活时间。
RabbitMQ可以对队列和消息分别设置TTL。
对队列设置就是队列没有消费者连着的保留时间,也可以对每⼀个单独的消息做单独的设置。
超过了这个时间,我们认为这个消息就死了,称之为死信。
如果队列设置了,消息也设置了,那么会取⼩的。
所以⼀个消息如果被路由到不同的队列中,这个消息死亡的时间有可能不⼀样(不同的队列设置)。
这⾥单讲单个消息的TTL,因为它才是实现延迟任务的关键。
可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是⼀样的效果。
只是expiration字段是字符串参数,所以要写个int类型的字符串:当上⾯的消息扔到队列中后,过了3分钟,如果没有被消费,它就死了。
不会被消费者消费到。
这个消息后⾯的,没有“死掉”的消息对顶上来,被消费者消费。
死信在队列中并不会被删除和释放,它会被统计到队列的消息数中去。
单靠死信还不能实现延迟任务,还要靠Dead Letter Exchange。
Dead Letter ExchangesExchage的概念在这⾥就不在赘述。
⼀个消息在满⾜如下条件下,会进死信路由,记住这⾥是路由⽽不是队列,⼀个路由可以对应很多队列。
1. ⼀个消息被Consumer拒收了,并且reject⽅法的参数⾥requeue是false。
也就是说不会被再次放在队列⾥,被其他消费者使⽤。
2. 上⾯的消息的TTL到了,消息过期了。
3. 队列的长度限制满了。
排在前⾯的消息会被丢弃或者扔到死信路由上。
Dead Letter Exchange其实就是⼀种普通的exchange,和创建其他exchange没有两样。
RabbitMQ设置消息的TTL(过期时间)
RabbitMQ设置消息的TTL(过期时间)我们在RabbitMQ中发布消息时,在代码中有两种⽅法设置某个队列的消息过期时间:1、针对队列来说,可以使⽤x-message-ttl参数设置当前队列中所有消息的过期时间,即当前队列中所有的消息过期时间都⼀样;2、针对单个消息来说,在发布消息时,可以使⽤Expiration参数来设置单个消息的过期时间。
以上两个参数的单位都是毫秒,即1000毫秒为1秒。
如果以上两个都设置,则以当前消息最短的那个过期时间为准。
接下来让我们在在代码中相见!针对队列来说://⾸先创建⼀个连接⼯⼚对象var factory = new ConnectionFactory() { HostName = "localhost", UserName = "yyt", Password = "yyt888888",VirtualHost="log" };//然后使⽤⼯⼚对象创建⼀个TCP连接using (var connection = factory.CreateConnection()){//在当前连接上创建⼀根通信的虚拟管道using (var channel = connection.CreateModel()) {//声明⼀个交换机channel.ExchangeDeclare("e.log", "direct");//声明⼀个队列,设置arguments的参数x-message-ttl为10000毫秒channel.QueueDeclare(queue: "q.log.error",durable: false,exclusive: false,autoDelete: false,arguments: new Dictionary<string, object> {{ "x-message-ttl",10000}//x-message-ttl即设置当前队列消息的过期时间。
rabbitmq的使用方法
rabbitmq的使用方法RabbitMQ是一个开源的消息代理软件,用于实现异步消息传递。
以下是使用RabbitMQ的一些基本方法:1. 安装和配置:首先,你需要从RabbitMQ的官网下载并安装RabbitMQ 服务器。
安装完成后,你可以通过浏览器访问RabbitMQ的管理界面,进行基本的配置。
2. 创建队列:在RabbitMQ中,消息被存储在队列中。
你可以使用RabbitMQ的管理界面或者通过编程的方式创建队列。
例如,使用Python 的pika库,你可以这样创建一个队列:```pythonimport pikaconnection = (('localhost'))channel = ()_declare(queue='hello')()```3. 发送消息:一旦你创建了队列,你就可以开始发送消息到这个队列。
同样使用pika库,你可以这样发送消息:```pythonimport pikaconnection = (('localhost'))channel = ()_publish(exchange='', routing_key='hello', body='Hello World!') ()```4. 接收消息:要接收消息,你需要创建一个消费者来从队列中获取消息。
消费者可以是任何能够处理RabbitMQ消息的应用程序。
例如,你可以创建一个Python消费者来接收消息:```pythonimport pikaconnection = (('localhost'))channel = ()_declare(queue='hello')def callback(ch, method, properties, body):print(f" [x] Received {body}")_consume(queue='hello', on_message_callback=callback,auto_ack=True)print(' [] Waiting for messages. To exit press CTRL+C')_consuming()```5. 确认消息处理:在RabbitMQ中,你可以选择自动确认(auto_ack)或手动确认(manual_ack)消息处理。
rabbitmq延时队列实现原理
rabbitmq延时队列实现原理以rabbitmq延时队列实现原理为题,本文将对rabbitmq延时队列的实现原理进行详细介绍。
一、什么是延时队列延时队列是一种特殊的消息队列,用于延迟处理消息。
在实际应用中,我们经常会遇到需要在一定时间后执行某个操作的场景,比如订单超时未支付自动取消等。
延时队列就是为了解决这类问题而设计的。
二、rabbitmq延时队列的基本原理rabbitmq延时队列的实现原理其实并不复杂,主要涉及到以下几个组件:1. 生产者:负责产生需要延迟处理的消息,并将消息发送到延时队列中。
2. 延时队列:用于存储需要延时处理的消息,一般是一个普通的消息队列。
3. 消息消费者:负责从延时队列中取出消息,并进行相应的处理。
具体的实现步骤如下:1. 创建一个普通的消息队列,用于存储需要延时处理的消息。
2. 设置消息的过期时间为需要延时的时间,即消息在队列中的存活时间。
3. 创建一个消费者来监听延时队列,当消息过期后,消费者会从队列中取出消息并进行处理。
三、延时队列的实现方法在rabbitmq中,可以通过两种方式来实现延时队列。
1. 使用TTL(Time to Live)和DLX(Dead Letter Exchanges)机制TTL是消息的生存时间,即消息存活在队列中的时间。
DLX则是一种特殊的交换机,用于接收过期消息并转发到指定的队列中。
具体实现步骤如下:1. 创建一个普通的消息队列,并设置队列的TTL。
2. 创建一个DLX交换机,并将该队列绑定到DLX交换机上。
3. 设置DLX交换机的路由规则,将过期消息转发到指定的队列中。
4. 创建一个消费者来监听指定的队列,当消息过期后,消费者会从队列中取出消息并进行处理。
2. 使用rabbitmq-delayed-message-exchange插件rabbitmq-delayed-message-exchange是rabbitmq的一个插件,可以在rabbitmq中直接使用该插件来实现延时队列,而不需要像方式一那样手动配置。
延迟任务的实现总结
延迟任务的实现总结上一篇写了使用RabbitMQ来实现延迟任务的实现,其实实现延迟任务的方式有很多,各有利弊,有单机和分布式的。
在这里做一个总结,在遇到这类问题的时候希望给大家一个参考和思路。
延迟任务有别于定式任务,定式任务往往是固定周期的,有明确的触发时间。
而延迟任务一般没有固定的开始时间,它常常是由一个事件触发的,而在这个事件触发之后的一段时间内触发另一个事件。
延迟任务相关的业务场景如下:场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时。
场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单。
下面我们来探讨一些方案,其实这些方案没有好坏之分,和系统架构一样,只有最适合。
对于数据量较小的情况下,任意一种方案都可行,考虑的是简单明了和开发速度,尽量避免把系统搞复杂了。
而对于数据量较大的情况下,就需要有一些选择,并不是所有的方案都适合了。
1. 数据库轮询这是比较常见的一种方式,所有的订单或者所有的命令一般都会存储在数据库中。
我们会起一个线程去扫数据库或者一个数据库定时Job,找到那些超时的数据,直接更新状态,或者拿出来执行一些操作。
这种方式很简单,不会引入其他的技术,开发周期短。
如果数据量比较大,千万级甚至更多,插入频率很高的话,上面的方式在性能上会出现一些问题,查找和更新对会占用很多时间,轮询频率高的话甚至会影响数据入库。
一种可以尝试的方式就是使用类似TBSchedule或Elastic-Job这样的分布式的任务调度加上数据分片功能,把需要判断的数据分到不同的机器上执行。
如果数据量进一步增大,那扫数据库肯定就不行了。
另一方面,对于订单这类数据,我们也许会遇到分库分表,那上述方案就会变得过于复杂,得不偿失。
2. JDK 延迟队列Java中的DelayQueue位于java.util.concurrent 包下,作为单机实现,它很好的实现了延迟一段时间后触发事件的需求。
Java实现RabbitMq延时队列和死信队列
Java实现RabbitMq延时队列和死信队列延时队列:实际是不存在直接可⽤的延时队列,可通过死信消息和死信队列来实现延时队列的功能。
死信交换机: DLX 全称(Dead-Letter-Exchange)。
其实它是个普通的交换机,但它是设置在队列上某个参数的值对应的交换机。
死信队列:如果某个队列上存在参数:x-dead-letter-exchange,当这个队列⾥的消息变成死信消息(dead message)后会被重新Pushlish到 x-dead-letter-exchange 所对应参数值的交换机上,跟这个交换机所绑定的队列就是死信队列。
死信消息:消息被拒绝(basic.reject / basic.nack),并且requeue = false消息TTL过期队列达到了最⼤的长度时过期消息:RabbitMq 有两种设置消息过期的⽅式:创建队列时通过 x-message-ttl 参数指定该队列消息的过期时间,这种队列⾥的消息过期时间全部相同。
⽣产者Pushlish消息时,通过设置消息的 expiration 参数指定过期时间,每个消息的过期时间都不⼀样。
如果两者同时使⽤,过期时间按照⼩的⼀⽅为准,两种⽅式设置的时间都是毫秒。
应⽤场景:延时队列的应⽤场景很多,在我的项⽬开发中也涉及到很多,例如:订单五分钟未⽀付⾃动取消、订单准备超时30分钟推送提醒给门店、订单完成后两⼩时推送评价邀请给⽤户等等,这些间隔指定时间后的操作都可以使⽤延时队列。
上⼀篇⽂章:介绍了RabbitMq的基本操作,要引⼊的包和配置可以参考上⼀篇⽂章。
这⾥就利⽤RabbitMq的死信队列直接来实现延时队列的功能。
⾸先创建⼀个⾃动加载类利⽤Bean在项⽬启动时,⾃动创建延时和死信交换机/延时和死信队列,并将创建好的队列绑定在对应的交换机上。
如果交换机和队列存在的情况下,则不会创建或更新。
这⼀步可减少⼿动或忘记创建队列带来的⿇烦:package com.demo.www.rabbitmq.config;import mon.collect.Maps;import lombok.extern.slf4j.Slf4j;import org.springframework.amqp.core.*;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.Map;/*** RabbitMq 延时队列实现* @author AnYuan*/@Slf4j@Configurationpublic class DelayQueueConfig {/*** 延迟队列*/public static final String DELAY_EXCHANGE = "delay.queue.business.exchange";public static final String DELAY_QUEUE = "delay.queue.business.queue";public static final String DELAY_QUEUE_ROUTING_KEY = "delay.queue.business.queue.routingKey";/*** 死信队列*/public static final String DEAD_LETTER_EXCHANGE = "delay.queue.deadLetter.exchange";public static final String DEAD_LETTER_QUEUE_ROUTING_KEY = "delay.queue.deadLetter.delay_10s.routingKey";public static final String DEAD_LETTER_QUEUE = "delay.queue.deadLetter.queue";/*** 声明死信交换机* @return deadLetterExchange*/@Beanpublic DirectExchange deadLetterExchange() {return new DirectExchange(DEAD_LETTER_EXCHANGE);}/*** 声明死信队列⽤于接收死信消息* @return deadLetterQueueA*/@Beanpublic Queue deadLetterQueueA() {return new Queue(DEAD_LETTER_QUEUE);}/*** 将死信队列绑定到死信交换机上* @return deadLetterBindingA*/@Beanpublic Binding deadLetterBindingA() {return BindingBuilder.bind(deadLetterQueueA()).to(deadLetterExchange()).with(DEAD_LETTER_QUEUE_ROUTING_KEY);}/*** 声明延时交换机* @return delayExchange*/@Beanpublic DirectExchange directExchange() {return new DirectExchange(DELAY_EXCHANGE);}/*** 将延时队列绑定参数* @return Queue*/@Beanpublic Queue delayQueueA() {Map<String, Object> maps = Maps.newHashMapWithExpectedSize(3);// 队列绑定DLX参数(关键⼀步)maps.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);// 队列绑定死信RoutingKey参数maps.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUE_ROUTING_KEY);// 消息过期采⽤第⼀种设置队列的 ttl 时间,消息过期时间全部相同。
RabbitMQ延时消息实现方案(岳小均)
最终选择了方案一1.4. 操作步骤4.1. 建立 delay.exchange注意事项:不要设置为Internal,否则将无法接受dead letter4.2. 建立延时队列(delay queue)1. 2. 3. 4. 5. 6.7. 注意事项:按照延期时间配置queue的名字,建议命名规则为delay.{time}.queue,使用delay前缀方便排序和做一些权限控制cos线上集群默认配置了delay.1m.queue、delay.5m.queue、delay.15m.queue三个队列通过TTL设置队列的延期时间,对应不同的QueueMAX length为最大的积压的消息个数,推荐设置为100w~500w之间,推测方法见积压测试结果,超过MAX length限制以后,队列头部的消息会立即转到delay.exchange进行投递,不会造成消息丢失设置dead letter exchange为刚才配置的 delay.exchange注意不要配置"dead letter routing key"否则会覆盖掉消息发送时携带的routingkey,导致后面无法路由4.3. 配置延时路由规则4.3.1. 需要延时的消息到exchange后先路由到指定的延时队列4.3.2. delay.exchange 再重新把消息路由到正常的queue或exchang中4.3.3. 消费者和以前一样从正常queue中接收消费消息5. 积压消息测试结果5.1. 积压测试结论从实现原理上看,对于持久化消息,内存主要保存的是消息的索引数据,从测试结果也可以验证,可以得出以下数据:1. a. i. ii. b. c.2. a. b.3. 1. 从实现原理上看,对于持久化消息,内存主要保存的是消息的索引数据,从测试结果也可以验证,可以得出以下数据:内存占用方面估算消息占用内存的大小确实和消息体本身大小无关,和消息个数直接相关。
RabbitMQ延迟队列
RabbitMQ延迟队列延迟队列延迟队列⼜被称为延时队列、死信队列,它也是 RabbitMQ 队列中的⼀种,指进⼊该队列中的消息会被延迟消费的队列。
顾名思义,延迟队列和普通队列的区别在于:进⼊普通队列的消息将会⽴即『⾛向』下⼀个环节,⽽下⼀个环节就是消费者;⽽进⼊延迟队列的消息将会被延迟队列『持有』若⼲时间,⽽后才『⾛向』下⼀个环节,⽽且下⼀个环节是另⼀个交换机。
这个『另⼀个交换机』也被称为死信交换机。
RabbitMQ 引⼊延迟队列主要是⽤于『延迟』⼀定时间再处理特定的业务逻辑,⽽这种『延迟』在 RabbitMQ 看来是『⾃动化』的,⽆须⼈为进⾏⼲预。
延迟队列的使⽤价值在于:1. 某些业务需要这种机制。
例如,订单 30 分钟内未⽀付则需要取消订单。
2. 在某种程度上,它可以替代定时任务。
1. 专有词汇与普通的队列⼀样,延迟队列也具有消息、交换机、路由和队列等名词。
不过,它还增加了 3 个专有名词:DLXDead Letter Exchange,死信队列交换机,是⼀种特殊类型的交换机。
DLKDead Letter Routing-Key,死信路由,同样也是⼀种特殊类型的路由。
主要是和 DLX 组合在⼀起构成死信队列。
TTLTime To Live,指进⼊延迟队列中的消息可以存活的时间。
当 TTL ⼀到,将意味着该消息『死了』,从⽽进⼊下⼀个『中转站』,等待被真正的消息队列监听消费。
普通队列 + 三个特殊设置 = 延迟队列/死信队列Dead letter exchange:x-dead-letter-exchange 。
指定延迟队列的『下家』交换机。
Dead letter routing key:x-dead-letter-routing-key 。
延迟队列⾃动向『下家』交换机投递消息时所使⽤的消息的 routing-key。
Message TTL:x-message-ttl 。
延迟队列要持有消息的时长。
PHPRabbitMQ实现简单的延迟队列
PHPRabbitMQ实现简单的延迟队列1.TTL+死信队列(DLX)实现TTL(x-message-ttl)是指队列中的消息在丢弃之前的可存活时间。
死信队列是放置没有被成功消费且超过了TTL⽣存时间消息的队列,如果消息没有在指定的TTL时间内被成功消费,并且给需要延迟执⾏的队列绑定了死信交换机和死信队列,将信息publish到死信交换机中后可被绑定交换机的死信队列消费,利⽤这⼀特性可以实现延迟队列。
消息队列中的消息会在⼀下⼏种情况下变成死信消息被拒绝(basic.reject / basic.nack),并且requeue = false;消息TTL过期;队列达到最⼤长度;在声明被延迟的任务队列前,需要配置如下参数。
x-message-ttl设置队列中消息的⽣存期,超过这个时间消息将变成死信,也可以在单条消息publish的时候设置ttl,rabbitmq会取两者中较⼩者。
$arguments = ['x-message-ttl' => 6000, //消息在丢弃之前的可存活时间'x-dead-letter-exchange' => $deadExchangeName, //死信发送的交换机名字'x-dead-letter-routing-key' => $deadRouteKey, //死信的路由键];$queue->setArguments($arguments);消费者代码//创建连接和channel$connect = new AMQPConnection($config);if (!$connect->connect()) {die("Cannot connect to the broker!\n");}$channel = new AMQPChannel($connect);//**********************创建⼀个⽤于存放死信的交换机和队列*************$deadExchangeName = 'dead_exchange';$deadQueueName = 'delayed_order';$deadRouteKey = 'delayed_order';$deadExchange = new AMQPExchange($channel);$deadExchange->setName($deadExchangeName);$deadExchange->setType(AMQP_EX_TYPE_DIRECT);$deadExchange->declareExchange();$deadQueue = new AMQPQueue($channel);$deadQueue->setName($deadQueueName);$deadQueue->declareQueue();$deadQueue->bind($deadExchange->getName(), $deadRouteKey);//***********************创建被延迟的交换机和消息队列********************$exchangeName = 'exchange1';$queueName = 'order';$routeKey = 'order';$exchange = new AMQPExchange($channel);$exchange->setName($exchangeName);$exchange->setType(AMQP_EX_TYPE_DIRECT);// 1:不持久化到磁盘,宕机数据消失 2:持久化到磁盘// $exchange->setFlags(AMQP_DURABLE);// 声明交换机$exchange->declareExchange();// 创建消息队列$queue = new AMQPQueue($channel);$queue->setName($queueName);$arguments = ['x-message-ttl' => 6000,'x-dead-letter-exchange' => $deadExchangeName, //死信发送的交换机'x-dead-letter-routing-key' => $deadRouteKey, //死信routeKey];// 设置持久性// $queue->setFlags(AMQP_DURABLE);$queue->setArguments($arguments);// 声明消息队列$queue->declareQueue();$queue->bind($exchange->getName(), $routeKey);// 向服务器队列推送10条消息$msg = 'hello world 1';$exchange->publish($msg, $routeKey, AMQP_NOPARAM, ['delivery_mode' => 2]);⽣产者代码$exchangeName = 'dead_exchange';$queueName = 'delayed_order';$routeKey = 'delayed_order';//创建连接和channel$connect = new AMQPConnection($config);if (!$connect->connect()) {die("Cannot connect to the broker!\n");}$channel = new AMQPChannel($connect);$exchange = new AMQPExchange($channel);$exchange->setName($exchangeName);$exchange->setType(AMQP_EX_TYPE_DIRECT);// 1:不持久化到磁盘,宕机数据消失 2:持久化到磁盘// $exchange->setFlags(AMQP_DURABLE);// 声明交换机$exchange->declareExchange();// 创建消息队列$queue = new AMQPQueue($channel);$queue->setName($queueName);// $queue->setArgument('x-message-ttl', 5000);// 设置持久性// $queue->setFlags(AMQP_DURABLE);// 声明消息队列$queue->declareQueue();$queue->bind($exchange->getName(), $routeKey);// 接收消息并处理回调$queue->consume('receive');// 处理回调的⽅法function receive($envelop, $queue){echo $envelop->getBody() . "\n";// ACK 通知⽣产者任务完成$queue->ack($envelop->getDeliveryTag(), AMQP_NOPARAM);}。
RabbitMQ使用prefetch_count优化队列的消费,使用死信队列和延迟队列实现。。。
RabbitMQ使⽤prefetch_count优化队列的消费,使⽤死信队列和延迟队列实现。
RabbitMQ 的优化channel⽣产者,消费者和 RabbitMQ 都会建⽴连接。
为了避免建⽴过多的 TCP 连接,减少资源额消耗。
AMQP 协议引⼊了信道(channel),多个 channel 使⽤同⼀个 TCP 连接,起到对 TCP 连接的复⽤。
不过 channel 的连接数是有上限的,过多的连接会导致复⽤的 TCP 拥堵。
const (maxChannelMax = (2 << 15) - 1defaultChannelMax = (2 << 10) - 1)prefetch Count什么是prefetch Count,先举个栗⼦:假定 RabbitMQ 队列有 N 个消费队列,RabbitMQ 队列中的消息将以轮询的⽅式发送给消费者。
消息的数量是 M,那么每个消费者得到的数据就是 M%N。
如果某⼀台的机器中的消费者,因为⾃⾝的原因,或者消息本⾝处理所需要的时间很久,消费的很慢,但是其他消费者分配的消息很快就消费完了,然后处于闲置状态,这就造成资源的浪费,消息队列的吞吐量也降低了。
这时候prefetch Count就登场了,通过引⼊prefetch Count来避免消费能⼒有限的消息队列分配过多的消息,⽽消息处理能⼒较好的消费者没有消息处理的情况。
RabbitM 会保存⼀个消费者的列表,每发送⼀条消息都会为对应的消费者计数,如果达到了所设定的上限,那么 RabbitMQ 就不会向这个消费者再发送任何消息。
直到消费者确认了某条消息之后 RabbitMQ 将相应的计数减1,之后消费者可以继续接收消息,直到再次到达计数上限。
这种机制可以类⽐于TCP/IP中的"滑动窗⼝"。
所以消息不会被处理速度很慢的消费者过多霸占,能够很好的分配到其它处理速度较好的消费者中。
Spring集成RabbitMQ并实现延迟队列
Spring集成RabbitMQ并实现延迟队列一、说明在实际业务场景中可能会用到延时消息发送,例如异步回调失败时的重发机制。
RabbitMQ本身不具有延时消息队列的功能,但是可以通过rabbitmq-delayed-message-exchange来实现(也可以通过TTL(Time To Live)、DLX(Dead Letter Exchanges)特性实现,我们主要讲解通过延迟插件来实现的方法)。
利用RabbitMQ的这种特性,应该可以实现很多现实中的业务,我们可以发挥想象。
二、安装插件RabbitMQ的安装请参考我的文章“RabbitMQ安装与使用”,这里我们重点讲插件的安装。
首先到/community-plugins.html网页下载适合的“rabbitmq_delayed_message_exchange插件”。
下载完成后将它放到RabbitMQ插件安装目录({rabbitmq-server}/plugins/),然后执行命令rabbitmq-plugins enable rabbitmq_delayed_message_exchange启用插件,执行命令rabbitmq-plugins disable rabbitmq_delayed_message_exchange 也可以关闭插件。
具体过程可以查看参考文档2。
三、Spring集成RabbitMQ1、maven配置[html] view plain copyprint?1.<dependency>2.<groupId>org.springframework.amqp</groupId>3.<artifactId>spring-amqp</artifactId>4.<version>1.6.6.RELEASE</version>5.<exclusions>6.<exclusion>7.<groupId>org.springframework</groupId>8.<artifactId>spring-core</artifactId>9.<version>4.1.6.RELEASE</version>10.</exclusion>11.</exclusions>12.</dependency>13.<dependency>14.<groupId>org.springframework.amqp</groupId>15.<artifactId>spring-rabbit</artifactId>16.<version>1.6.6.RELEASE</version>17.<exclusions>18.<exclusion>19.<groupId>org.springframework</groupId>20.<artifactId>spring-core</artifactId>21.<version>4.1.6.RELEASE</version>22.</exclusion>23.<exclusion>24.<groupId>org.springframework</groupId>25.<artifactId>spring-messaging</artifactId>26.<version>4.1.6.RELEASE</version>27.</exclusion>28.<exclusion>29.<groupId>org.springframework</groupId>30.<artifactId>spring-tx</artifactId>31.<version>4.1.6.RELEASE</version>32.</exclusion>33.<exclusion>34.<groupId>org.springframework</groupId>35.<artifactId>spring-context</artifactId>36.<version>4.1.6.RELEASE</version>37.</exclusion>38.</exclusions>39.</dependency>说明:实现延迟队列需要Spring在4.1以上,spring-amqp在1.6以上。
mq延时队列用法
mq延时队列用法一、什么是延时队列延时队列是一种特殊的消息队列,用于在一定的时间延迟后才将消息发送给消费者。
它可以用于解决一些需要延迟处理的场景,如订单超时未支付自动取消、消息重试等。
二、为什么需要延时队列在实际应用开发中,有许多场景需要延迟处理消息。
例如,用户下单后需要等待一定时间才能自动取消订单,这就需要延时队列来实现。
延时队列可以提高系统的可靠性和稳定性,同时减少人工干预的成本。
三、常见的延时队列实现方式1. 基于消息中间件的延时队列常见的消息中间件如 RabbitMQ、Kafka、ActiveMQ 等都支持延时队列的实现。
它们通过设置消息的 TTL(Time To Live)来实现延时功能。
当消息进入队列后,会根据设置的 TTL 值进行延时,然后再发送给消费者进行处理。
2. 使用定时任务实现延时队列除了使用消息中间件,还可以通过定时任务来实现延时队列。
在这种方式下,消息会在一定的时间间隔后被消费者处理。
可以使用定时任务框架如 Quartz 或Spring Task 来实现。
3. 基于数据库的延时队列还可以利用数据库的特性来实现延时队列。
通过在数据库中保存消息和处理时间,然后定时查询数据库,找出需要处理的消息进行消费。
这种方式相对简单,但对数据库的性能要求较高。
四、延时队列的使用场景延时队列可以应用于各种场景,以下是一些常见的使用场景:1. 订单超时未支付自动取消在电商系统中,用户下单后需要一定时间内完成支付,否则订单会被自动取消。
可以通过延时队列来实现订单超时自动取消的功能。
当用户下单后,将订单信息发送到延时队列中,设置延时时间为订单超时时间,当延时时间到达后,订单会自动取消。
2. 消息重试在分布式系统中,消息的发送和处理可能会出现失败的情况。
为了保证消息的可靠性,可以使用延时队列来实现消息的重试机制。
当消息发送失败时,将消息发送到延时队列中,设置延时时间为下一次重试的时间间隔,当延时时间到达后,消息会再次被发送。
基于RabbitMQ的延迟队列实现及应用
基于RabbitMQ的延迟队列实现及应用作者:黄可王盛义胡兵李朝阳易勇来源:《科学导报·学术》2020年第49期摘要:延迟队列是一种延迟处理消息的特殊队列,在实际应用系统中,存在大量需要延迟处理业务的场景。
常规的轮询检测实现方式,存在诸多弊端。
本文结合具体业务场景,利用RabbitMQ消息中间件的死信机制,设计实现了一种简单优雅的延迟队列。
关键词:延迟队列;RabbitMQ;死信1.RabbitMQ和延迟队列1.1RabbitMQ中间件RabbitMQ是当下非常流行的开源消息队中间件,使用erlang语言开发,实现了AMQP协议。
由于其性能稳定,维护更新快,社区活跃,广泛应用于众多企业信息系统中。
RabbitMQ的工作原理[1]如圖1所示。
其中P表示消息的生产者(Producer),X表示交换机(Exchange),Q表示队列(Queue),C表示消息的消费者(Consumer)。
AMQP协议规定的消息系统应包括消息生产者、消息消费者和消息服务器三个核心功能组件。
其中RabbitMQ服务器上可以划分为多个虚拟主机(vhost),每个虚拟机都可以创建交换机和队列,虚拟机之间的交换机和队列数据是互相隔离的。
交换机接收消息生产者发布的消息,并根据规则将消息路由给符合条件的队列。
交换机是匹配和路由消息的实体。
队列是存储和转发消息的实体,队列中的消息会被顺序传递给一个或多个消费者。
绑定(binding)用于表示交换机和消息队列(或交换机)之间的关系。
消息生产者发送消息时提供了消息的路由参数(routing key),交换机收到消息后匹配绑定规则,然后将消息投递到指定的队列。
1.2延迟队列延迟队列是一种特殊的队列,和一般的队列不同,延迟队列中的消息不会被立即消费,往往需要延迟一定的时间后才会被消费。
延迟队列中的消息有别于定时任务,定时任务通常是有明确的触发时间和触发周期的。
而延迟队列中的消息没有固定的开始时间,消息进入延迟队列后间隔指定的时间执行。
RabbitMq实现延时队列-Springboot版本
RabbitMq实现延时队列-Springboot版本rabbitmq本⾝没有实现延时队列,但是可以通过死信队列机制,⾃⼰实现延时队列;原理:当队列中的消息超时成为死信后,会把消息死信重新发送到配置好的交换机中,然后分发到真实的消费队列;步骤:1、创建带有时限的队列 dealLineQueue;2、创建死信Faout交换机dealLineExchange;3、创建消费队列realQueue,并和dealLineExchange绑定4、配置dealLineQueue 的过期时间,消息过期后的死信交换机,重发的routing-key;以下以springboot为例⼦贴出代码项⽬结构:基本值-DealConstantpackage com.eyjian.rabbitmq.dealline;public interface DealConstant {String DEAL_LINE_QUEUE = "dealLineQueue";String DEAL_LINE_EXCHANGE = "dealLineExchange";String REAL_QUEUE= "realQueue";}消费者Listerpackage com.eyjian.rabbitmq.dealline;import org.springframework.amqp.core.Message;import org.springframework.amqp.rabbit.annotation.RabbitListener;import ponent;/*** 死信队⾥模拟延时队列* @Author: yeyongjian* @Date: 2019-05-18 14:12*/@Componentpublic class Lister {@RabbitListener(queues = DealConstant.REAL_QUEUE)public void handle(Message message){byte[] body = message.getBody();String msg = new String(body);System.out.println(msg);}}配置类RabbitmqConfigpackage com.eyjian.rabbitmq.dealline;import com.rabbitmq.client.Channel;import org.springframework.amqp.core.Binding;import org.springframework.amqp.core.BindingBuilder;import org.springframework.amqp.core.FanoutExchange;import org.springframework.amqp.core.Queue;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.io.IOException;import java.util.HashMap;import java.util.Map;@Configurationpublic class RabbitmqConfig {@Autowiredprivate RabbitTemplate rabbitTemplate;//启动初始化删除绑定⽤的// @PostConstructpublic void delete() throws IOException {Channel channel = rabbitTemplate.getConnectionFactory().createConnection().createChannel(true); channel.queueUnbind(DealConstant.REAL_QUEUE,DealConstant.DEAL_LINE_EXCHANGE,""); }@Beanpublic Queue initDealLineQueue() {Map<String, Object> args = new HashMap<>();args.put("x-dead-letter-exchange", DealConstant.DEAL_LINE_EXCHANGE);args.put("x-dead-letter-routing-key", DealConstant.DEAL_LINE_QUEUE);//超时转发的队列args.put("x-message-ttl", 5000);//延时时间Queue queue = new Queue(DealConstant.DEAL_LINE_QUEUE,true,false,false,args);return queue;}@BeanFanoutExchange dealLineExchange() {return new FanoutExchange(DealConstant.DEAL_LINE_EXCHANGE);}@BeanBinding bindingiVewUgcTopicExchange(Queue initRealQueue, FanoutExchange dealLineExchange) { return BindingBuilder.bind(initRealQueue).to(dealLineExchange);}@Beanpublic Queue initRealQueue() {return new Queue(DealConstant.REAL_QUEUE);}}application.properties⽂件spring.rabbitmq.addresses=localhostspring.rabbitmq.host=5672ername=guestspring.rabbitmq.password=guest项⽬启动后,rabbitmq控制台信息如下:test类发送消息package com.eyjian.rabbitmq;import com.eyjian.rabbitmq.dealline.DealConstant;import com.rabbitmq.client.Channel;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.io.IOException;@RunWith(SpringRunner.class)@SpringBootTestpublic class RabbitmqLearnApplicationTests {@AutowiredRabbitTemplate rabbitTemplate;@Testpublic void contextLoads() throws IOException {rabbitTemplate.convertAndSend(DealConstant.DEAL_LINE_QUEUE,"hell word"); }}5秒后控制台打印消息。
RabbitMQ使用延迟队列(通俗易懂)
RabbitMQ使⽤延迟队列(通俗易懂)场景延迟消息是指的消息发送出去后并不想⽴即就被消费,⽽是需要等(指定的)⼀段时间后才触发消费。
订单创建成功后,需要30分钟内⽀付成功。
就可以⽤延迟队列,订单创建成功后发送⼀个延迟消息,这条消息30分钟后才能被消费,消费的时候去查询订单状态是否是已⽀付。
公司预约会议,22点有会议,21:45(提前15分钟)就通知参会⼈员最好准备,快开会了。
实现⽅式延迟队列在AMQP协议和RabbitMQ中都没有相关的规定和实现。
可以借助“死信队列”来变相的实现(消息到期后加⼊死信队列,然后定义⼀个消费者消费死信队列的消息即可。
PS:代码中有完整实现);可以使⽤rabbitmq_delayed_message_exchange插件实现。
下⾯我们⽤插件的⽅式来实现延迟队列延迟消息消费流程:1. ⽣产者将消息(msg)和路由键(routekey)发送指定的延迟交换机(exchange)上2. 延迟交换机(exchange)存储消息等待消息到期根据路由键(routekey)找到绑定⾃⼰的队列(queue)并把消息给它(消息到期后才会到达队列)3. 队列(queue)再把消息发送给监听它的消费者(customer)⽂末有代码git地址(包含路由(Direct)模式、Work模式、主题(Topic)模式、发布订阅/⼴播(Fanout)模式、TTL、死信队列、延迟队列)安装延迟队列插件安装RabbitMQ教程:1、下载插件下载地址:https:///rabbitmq/rabbitmq-delayed-message-exchange/releases2、将需要安装的插件拷贝到插件位置/usr/local/Cellar/rabbitmq/3.9.7/plugins然后查看插件列表rabbitmq-plugins list结果如下[E*]、[e*]都是已安装的,其他都是未安装的3、启⽤插件rabbitmq-plugins enable rabbitmq_delayed_message_exchange再次查看插件列表4、重启RabbitMQ# 关闭RabbitMQrabbitmqctl stop# 后台启动RabbitMQrabbitmq-server -detachedSpringBoot整合RabbitMQ使⽤延迟队列1、添加依赖<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--amqp依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.12</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency></dependencies>2、添加配置⽂件application.ymlserver:port: 8899spring:rabbitmq:host: 127.0.0.1port: 5672username: guestpassword: guestpublisher-confirm-type: correlated # 消息发送到交换机确认机制,是否确认回调。
如何在MQ中实现支持任意延迟的消息?
如何在MQ中实现⽀持任意延迟的消息?什么是定时消息和延迟消息?定时消息:Producer 将消息发送到 MQ 服务端,但并不期望这条消息⽴马投递,⽽是推迟到在当前时间点之后的某⼀个时间投递到Consumer 进⾏消费,该消息即定时消息。
延迟消息:Producer 将消息发送到 MQ 服务端,但并不期望这条消息⽴马投递,⽽是延迟⼀定时间后才投递到 Consumer 进⾏消费,该消息即延时消息。
定时消息与延迟消息在代码配置上存在⼀些差异,但是最终达到的效果相同:消息在发送到 MQ 服务端后并不会⽴马投递,⽽是根据消息中的属性延迟固定时间后才投递给消费者。
⽬前业界MQ对定时消息和延迟消息的⽀持情况上图是阿⾥云上对业界MQ功能的对⽐,其中开源产品中只有阿⾥的RocketMQ⽀持延迟消息,且是固定的18个Level。
固定Level的含义是延迟是特定级别的,⽐如⽀持3秒、5秒的Level,那么⽤户只能发送3秒延迟或者5秒延迟,不能发送8秒延迟的消息。
消息队列RocketMQ的阿⾥云版本(收费版本)才⽀持到精确到秒级别的延迟消息(没有特定Level的限制)。
上图是CMQ中对MQ功能的对⽐,其中标明腾讯的CMQ⽀持延迟消息,但是没有具体写明⽀持到什么精度,⽀持任意时间还是特定的Level。
通过腾讯云上CMQ的API⽂档可以看到有⼀个秒级别的delaySeconds,应该是⽀持任意级别的延迟,即和收费版本的RocketMQ⼀致。
总结开源版本中,只有RocketMQ⽀持延迟消息,且只⽀持18个特定级别的延迟付费版本中,阿⾥云和腾讯云上的MQ产品都⽀持精度为秒级别的延迟消息(真是有钱能使⿁推磨啊,有钱就能发任意延迟的消息了,没钱最多只能发特定Level了)任意延迟的消息难点在哪⾥?开源版本没有⽀持任意延迟的消息,我想可能有以下⼏个原因:1. 任意延迟的消息的需求不强烈2. 可能是⼀个⽐较有技术含量的点,不愿意开源需求不强对⽀持任意延迟的需求确实不强,因为:1. 延迟并不是MQ场景的核⼼功能,业务单独做⼀个替代⽅案的成本不⼤2. 业务上⼀般对延迟的需求都是固定的,⽐如下单后半⼩时check是否付款,发货后7天check是否收货在我司,MQ上线⼀年多后才有业务⽅希望我能⽀持延迟消息,且不要求任意延迟,只要求和RocketMQ开源版本⼀致,⽀持⼀些业务上的级别即可。
延迟消息的五种实现方案
延迟消息的五种实现⽅案⽣产者把消息发送到消息队列中以后,并不期望被⽴即消费,⽽是等待指定时间后才可以被消费者消费,这类消息通常被称为延迟消息。
延迟消息的应⽤场景其实是⾮常的⼴泛,⽐如以下的场景:⽹上直播授课时,在课程开始前15分钟通知所有学⽣准备上课。
订单提交成功后1个⼩时内未⽀付,订单需要及时关闭并且释放对应商品的库存。
⽤户超过15天未登录时,给该⽤户发送召回推送。
⼯单提交后超过24⼩时未处理,向相关责任⼈发送催促处理的提醒。
针对延迟消息,本⽂向⼤家分享五种实现⽅案,下⾯我们就来逐⼀讨论各种⽅案的⼤致实现和优缺点。
⼀、Redis在Redis中,有⼀种有序集合(Sorted Set)的数据结构,在有序集合中,所有元素是按照其 Score 进⾏排序的。
我们可以把消息被消费的预期时间戳作为Score,定时任务不断读取Score⼤于当前时间的元素即可。
基本流程如下:1. 调⽤API,传⼊执⾏时间、消息体等数据。
2. ⽣成唯⼀key,把消息体数据序列化后存⼊Redis的String结构中。
3. 把key和执⾏时间的时间戳存⼊Redis的有序集合结构中,有序集合中不存储具体的消息体数据,⽽是存储唯⼀的key。
4. 定时任务不断读取时间戳最⼩的消息。
5. 如果时间戳⼩于当前时间,将key放⼊作为队列的Redis的List结构中。
6. 另外⼀个定时任务不断从队列中读取需要消费的消息的key。
7. 根据key获取消息体数据,对消息进⾏消费。
8. 如果消费消息成功,删除key对应的消息体数据。
9. 如果消费消息失败,重新存⼊key和时间戳(加60秒)。
具体⽅案如下图:为了避免⼀个有序集合中存储过多的延时消息,存⼊操作以及查询操作速度变慢的问题,可以建⽴多个有序集合,通过哈希算法把消息路由到不同的有序集合中去。
优点简单实⽤,快速落地。
缺点单个有序集合⽆法⽀持太⼤的数据量。
定时任务不断读取可能造成不必要的请求。
所以,Redis⽅案并不是⼀个⼗分成熟的⽅案,只是⼀个⽀持⼩消息量可以快速落地的⽅案。
C#通过rabbitmq实现定时任务(延时队列)
C#通过rabbitmq实现定时任务(延时队列)本⽂主要讲解如何通过RabbitMQ实现定时任务(延时队列)环境准备使⽤场景作为⼀个新的预⽀付订单被初始化放置,如果该订单在指定时间内未进⾏⽀付,则将被认为超时订单进⾏关闭处理;电商系统中应⽤较多,⽤户购买商品产⽣订单,但未进⾏⽀付,订单产⽣30分钟内未⽀付将关闭订单(且满⾜该场景数量庞⼤),不可能采⽤⼈⼯⼲预。
代码介绍⽣产者var factory = new ConnectionFactory(){Uri = new Uri("MQ地址")};using var connection = factory.CreateConnection();using var channel = connection.CreateModel();var exchangeName = "delay-exchange";var routingkey = "delay.delay";var queueName = "delay_queueName";//设置Exchange队列类型var argMaps = new Dictionary<string, object>(){{"x-delayed-type", "topic"}};//设置当前消息为延时队列channel.ExchangeDeclare(exchange: exchangeName, type: "x-delayed-message", true, false, argMaps);channel.QueueDeclare(queueName, true, false, false, argMaps);channel.QueueBind(queueName, exchangeName, routingkey);for (int i = 0; i < 3; i++){var time = 1000 * 5;var message = $@"发送时间为 {DateTime.Now:yyyy-MM-dd HH:mm:ss} 延时时间为:{time}";var body = Encoding.UTF8.GetBytes(message);var props = channel.CreateBasicProperties();//设置消息的过期时间props.Headers = new Dictionary<string, object>(){{ "x-delay", 5000 }};channel.BasicPublish(exchange: exchangeName,routingKey: routingkey,basicProperties: props,body: body);Console.WriteLine(message);}Console.ReadLine();消费者(⾃动绑定队列写法)var factory = new ConnectionFactory(){Uri = new Uri(MQ地址)};using var connection = factory.CreateConnection();using var channel = connection.CreateModel();var queueName = "delay_queueName";channel.QueueDeclare(queueName, true, false, false, null);var consumer = new EventingBasicConsumer(channel);consumer.Received += (model, ea) =>{var body = ea.Body;var message = Encoding.UTF8.GetString(body);var routingKey = ea.RoutingKey;Console.WriteLine($@"接受到消息的时间为 {DateTime.Now:yyyy-MM-dd HH:mm:ss},routingKey:{routingKey} message:{message} ");};channel.BasicConsume(queue: queueName,autoAck: true,consumer: consumer);Console.ReadLine();消费者(⼿动绑定队列写法)var factory = new ConnectionFactory(){Uri = new Uri(MQ地址)};using var connection = factory.CreateConnection();using var channel = connection.CreateModel();var exchangeName = "delay-exchange";var routingkey = "delay.delay";var queueName = "delay_queueName";var autoDelete = true;var argMaps = new Dictionary<string, object>(){{"x-delayed-type", "topic"}};channel.ExchangeDeclare(exchange: exchangeName, type: "x-delayed-message", true, false, argMaps);channel.QueueDeclare(queueName, true, false, false, argMaps);channel.QueueBind(queue: queueName, exchange: exchangeName, routingKey: routingkey);//channel.QueueDeclare(queueName, true, false, false, null);var consumer = new EventingBasicConsumer(channel);consumer.Received += (model, ea) =>{var body = ea.Body;var message = Encoding.UTF8.GetString(body);var routingKey = ea.RoutingKey;Console.WriteLine($@"接受到消息的时间为 {DateTime.Now:yyyy-MM-dd HH:mm:ss},routingKey:{routingKey} message:{message} ");};channel.BasicConsume(queue: queueName,autoAck: true,consumer: consumer);Console.ReadLine();最终实现效果(两个消费者)在上述实现中,其实主要靠以下参数来帮我们实现当前功能声明Exchange中的 type: "x-delayed-message" 这个表明当前队列为延时消息队列声明Exchange中arguments中的 {"x-delayed-type", "topic"} 当前表明当前队列为Topic模式最后我们在CreateBasicProperties的Header中设置 { "x-delay", 5000 }来达到消息延时的功能(单位为ms)建议如果使⽤当前模式来做定时任务,在要求消息不丢失的前提下,需要运维同学提供稳定的MQ环境到此这篇关于C#通过rabbitmq实现定时任务(延时队列)的⽂章就介绍到这了,更多相关C# rabbitmq定时任务内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
RabbitMQ入门教程(PHP版)使用rabbitmq-delayed-message-。。。
RabbitMQ⼊门教程(PHP版)使⽤rabbitmq-delayed-message-。
延迟任务应⽤场景场景⼀:物联⽹系统经常会遇到向终端下发命令,如果命令⼀段时间没有应答,就需要设置成超时。
场景⼆:订单下单之后30分钟后,如果⽤户没有付钱,则系统⾃动取消订单。
场景三:过1分钟给新注册会员的⽤户,发送注册邮件等。
php 使⽤rabbitmq-delayed-message-exchange插件实现延迟功能1.安装下载后解压,并将其拷贝⾄(使⽤Linux Debian/RPM部署)rabbitmq服务器⽬录:/usr/local/rabbitmq/plugins中( windows安装⽬录\rabbitmq_server-version\plugins ).2.启⽤插件使⽤命令rabbitmq-plugins enable rabbitmq_delayed_message_exchang启⽤插件rabbitmq-plugins enable rabbitmq_delayed_message_exchang输出如下:The following plugins have been enabled:rabbitmq_delayed_message_exchange通过rabbitmq-plugins list查看已安装列表,如下:...[ ] rabbitmq_delayed_message_exchange 20171215-3.6.x...3.机制解释安装插件后会⽣成新的Exchange类型x-delayed-message,该类型消息⽀持延迟投递机制,接收到消息后并未⽴即将消息投递⾄⽬标队列中,⽽是存储在mnesia(⼀个分布式数据系统)表中,检测消息延迟时间,如达到可投递时间时并将其通过x-delayed-type类型标记的交换机类型投递⾄⽬标队列。
4.php实现过程消费者 delay_consumer2.php:<?php//header('Content-Type:text/html;charset=utf8;');$params = array('exchangeName' => 'delayed_exchange_test','queueName' => 'delayed_queue_test','routeKey' => 'delayed_route_test',);$connectConfig = array('host' => 'localhost','port' => 5672,'login' => 'guest','password' => 'guest','vhost' => '/');//var_dump(extension_loaded('amqp'));//exit();try {$conn = new AMQPConnection($connectConfig);$conn->connect();if (!$conn->isConnected()) {//die('Conexiune esuata');//TODO 记录⽇志echo 'rabbit-mq 连接错误:', json_encode($connectConfig);exit();}$channel = new AMQPChannel($conn);if (!$channel->isConnected()) {// die('Connection through channel failed');//TODO 记录⽇志echo 'rabbit-mq Connection through channel failed:', json_encode($connectConfig);exit();}$exchange = new AMQPExchange($channel);//$exchange->setFlags(AMQP_DURABLE);//声明⼀个已存在的交换器的,如果不存在将抛出异常,这个⼀般⽤在consume端$exchange->setName($params['exchangeName']);$exchange->setType('x-delayed-message'); //x-delayed-message类型/*RabbitMQ常⽤的Exchange Type有三种:fanout、direct、topic。
优化RabbitMQ配置提升性能指南:策略与建议
优化RabbitMQ配置提升性能指南:策略与建议优化RabbitMQ的配置以提高性能可以按照以下步骤进行:1.调整队列属性:合理设置队列的参数,如最大长度(max-length)、最大内存限制(max-length-bytes)和消息过期时间(message-ttl)。
这些参数可以防止队列过度堆积消息,避免系统崩溃。
2.批量处理消息:消费者从队列中获取消息的方式会直接影响系统的性能。
建议将消息的获取和处理逻辑进行批量化,减少网络传输和消费者处理的开销。
3.水平扩展和负载均衡:如果队列的负载过重,可以考虑增加更多的消费者实例,并通过负载均衡策略将消息均匀地分发给各个消费者,提高系统的处理能力。
4.消息确认机制:在消息处理完毕后,必须及时向RabbitMQ发送确认消息(acknowledgment),告知消息已被消费。
这样可以确保消息不会重复消费,并减少队列的堆积。
5.引入缓存机制:为了提高消息处理的效率,可以引入缓存机制,将一部分消息缓存在内存中,避免频繁地访问磁盘。
6.集群和高可用性:如果系统要求具备高可用性,可以考虑搭建RabbitMQ集群。
通过多个节点的协同工作,实现负载均衡、故障转移和数据冗余,提高系统的可用性和稳定性。
7.网络连接与资源管理:建立合理的连接池来管理与RabbitMQ服务器的连接,避免频繁地创建和关闭连接。
通过重用连接,可以减少系统开销,提高性能。
同时,根据系统的负载情况,合理设置RabbitMQ节点所能够处理的最大连接数、最大通道数和最大队列数等资源限制。
8.资源限制与监控:根据系统的负载情况,合理设置RabbitMQ节点所能够处理的最大连接数、最大通道数和最大队列数等资源限制。
同时,通过监控工具实时监测系统的资源使用情况,及时发现并解决潜在的性能问题。
以上是优化RabbitMQ配置的一般步骤和建议,但具体优化策略需要根据实际的应用场景和需求来确定。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
最终选择了方案一
1.
4. 操作步骤
4.1. 建立 delay.exchange
注意事项:
不要设置为Internal,否则将无法接受dead letter
4.2. 建立延时队列(delay queue)
1. 2. 3. 4. 5. 6.
7. 注意事项:
按照延期时间配置queue的名字,建议命名规则为delay.{time}.queue,使用delay前缀方便排序和做一些权限控制cos线上集群默认配置了delay.1m.queue、delay.5m.queue、delay.15m.queue三个队列通过TTL设置队列的延期时间,对应不同的Queue
MAX length为最大的积压的消息个数,推荐设置为100w~500w之间,推测方法见积压测试结果,超过MAX length限制以后,队列头部的消息会立即转到delay.exchange进行投递,不会造成消息丢失设置dead letter exchange为刚才配置的 delay.exchange
注意不要配置"dead letter routing key"否则会覆盖掉消息发送时携带的routingkey,导致后面无法路由
4.3. 配置延时路由规则
4.3.1. 需要延时的消息到exchange后先路由到指定的延时队列
4.3.2. delay.exchange 再重新把消息路由到正常的queue或exchang中
4.3.3. 消费者和以前一样从正常queue中接收消费消息
5. 积压消息测试结果
5.1. 积压测试结论
从实现原理上看,对于持久化消息,内存主要保存的是消息的索引数据,从测试结果也可以验证,可以得出以下数据:
1. a. i. ii. b. c.
2. a. b.
3. 1. 从实现原理上看,对于持久化消息,内存主要保存的是消息的索引数据,从测试结果也可以验证,可以得出以下数据:
内存占用方面估算
消息占用内存的大小确实和消息体本身大小无关,和消息个数直接相关。
消息体为40字节的字符串,积压10万消息,占用101MB内存,23万消息,占用230MB内存消息体为一个整数,积压7万消息,占用64MB内存,24万消息,占用240MB内存
一个消息约占1KB的内存,以10GB内存,留一半余量:5GB/1K=500 0000
线上单个queue推荐线上的max length不要超过500万,根据延迟时间和消息量来调整此值的大小。
消息量估算
以30分钟延迟,每秒发送1000个消息为例,则最大值为:30*60*1000=180 0000
倒推:以500万,30分钟延迟为例,则每秒最多发送的消息个数为:5000000/30/60=2777
消息积压数超过max length以后,消息不会丢失,只。
会导致消息会被提前消费结论:
MAX length推荐这个值设置为,既可以满足业务需求,又不超过内存限制。
100万~500万5.2. 40字节消息积压26万
5.3. 4字节消息积压7万
5.4. 4字节消息积压24万
5.5. 4字节消息积压31万
5.6. 4字节消息积压64万
todo。