RabbitMQ基础(2)——发布订阅/fanout模式 topic模式 rabbitmq回调确认 延迟队列(死信)设计
目录
- 引出
- 点对点(simple)
- Work queues 一对多
- 发布订阅/fanout模式
- 以登陆验证码为例
- pom文件导包
- application.yml文件
- rabbitmq的配置
- 生产者生成验证码,发送给交换机
- 消费者消费验证码
- topic模式
- 配置类增加配置
- 生产者发送信息
- 进行发送
- 控制台查看
- rabbitmq回调确认
- 配置类
- 验证生产者发送是否成功
- 延迟队列(死信)设计
- java代码步骤
- 创建正常+死信队列
- 配置类+常量
- 生产者到正常队列
- 消费者进行延迟消费
- 延迟队列插件安装
- 访问官网
- 进入rabbitmq docker容器
- 上传到linux服务器
- 拷贝插件到容器中
- 进入容器安装插件
- 打开管理页面
- 总结
引出
1.rabbitmq队列方式的梳理,点对点,一对多;
2.发布订阅模式,交换机到消费者,以邮箱和手机验证码为例;
3.topic模式,根据规则决定发送给哪个队列;
4.rabbitmq回调确认,setConfirmCallback和setReturnsCallback;
5.死信队列,延迟队列,创建方法,正常—死信,设置延迟时间;
点对点(simple)
点对对方式传输
Work queues 一对多
1个生产者多个消费者
发布订阅/fanout模式
生产者通过fanout扇出交换机群发消息给消费者,同一条消息每一个消费者都可以收到。
以登陆验证码为例
pom文件导包
<!-- qq邮箱--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><!-- 阿里云短信验证码相关包--><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.5.3</version></dependency><!-- queue的包--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>
application.yml文件
server:port: 9099spring:# 模块的名字application:name: user-auth# 邮箱的配置mail:host: smtp.qq.comport: 587username: xxxxpassword: xxxxx# rabbitmq的配置rabbitmq:host: 192.168.111.130port: 5672username: adminpassword: 123logging:level:com.tianju.auth: debug
rabbitmq的配置
需要用到的常量
package com.tianju.auth.util;/*** rabbitmq的常量*/
public interface RabbitMqConstants {String MQ_MAIL_QUEUE="mq_email_queue";String MQ_PHONE_QUEUE="mq_phone_queue";String MQ_FANOUT_EXCHANGE="mq_fanout_exchange";// 参数 String name, boolean durable, boolean exclusive, boolean autoDeleteboolean durable = true;boolean exclusive = false;boolean autoDelete = false;}
RabbitMqConfig.java配置
邮箱队列,电话队列,交换机;
邮箱绑定交换机,电话绑定交换机;
创建队列参数说明:
参数 | 说明 |
---|---|
name | 字符串值,queue的名称。 |
durable | 布尔值,表示该 queue 是否持久化。 持久化意味着当 RabbitMQ 重启后,该 queue 是否会恢复/仍存在。 另外,需要注意的是,queue 的持久化不等于其中的消息也会被持久化。 |
exclusive | 布尔值,表示该 queue 是否排它式使用。排它式使用意味着仅声明他的连接可见/可用,其它连接不可见/不可用。 |
autoDelete | 布尔值,表示当该 queue 没“人”(connection)用时,是否会被自动删除。 |
不指定 durable、exclusive 和 autoDelete 时,默认为 true 、 false 和 false 。表示持久化、非排它、不用自动删除。
创建交换机参数说明
参数 | 说明 |
---|---|
name | 字符串值,exchange 的名称。 |
durable | 布尔值,表示该 exchage 是否持久化。 持久化意味着当 RabbitMQ 重启后,该 exchange 是否会恢复/仍存在。 |
autoDelete | 布尔值,表示当该 exchange 没“人”(queue)用时,是否会被自动删除。 |
不指定 durable 和 autoDelete 时,默认为
true
和false
。表示持久化、不用自动删除
package com.tianju.auth.config;import com.tianju.auth.util.RabbitMqConstants;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitMqConfig {@Bean // 邮箱的队列public Queue mailQueue(){return new Queue(RabbitMqConstants.MQ_MAIL_QUEUE,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete);}@Bean // 电话的队列public Queue phoneQueue(){return new Queue(RabbitMqConstants.MQ_PHONE_QUEUE,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete);}@Bean // 交换机public FanoutExchange fanoutExchange(){return new FanoutExchange(RabbitMqConstants.MQ_FANOUT_EXCHANGE,RabbitMqConstants.durable,RabbitMqConstants.autoDelete);}@Beanpublic Binding mailBinding(){return BindingBuilder.bind(mailQueue()).to(fanoutExchange());}@Beanpublic Binding phoneBinding(){return BindingBuilder.bind(phoneQueue()).to(fanoutExchange());}}
生产者生成验证码,发送给交换机
接口
package com.tianju.auth.service;public interface IUserService {/*** 生产者生成信息发送给交换机* @param msg 信息,这里是验证码*/void sendCode(String msg);
}
实现
package com.tianju.auth.service.impl;import com.tianju.auth.service.IUserService;
import com.tianju.auth.util.RabbitMqConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
@Slf4j
public class UserServiceImpl implements IUserService {@Autowiredprivate RabbitTemplate rabbitTemplate;@Overridepublic void sendCode(String msg) {rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_FANOUT_EXCHANGE,"routingkey.fanout",msg);log.debug("[生产者向交换机:] 发送一条信息:{}",msg);}}
测试类生成验证码,发给交换机
package com.tianju.auth.service.impl;import cn.hutool.core.lang.Snowflake;
import com.tianju.auth.service.IUserService;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)public class UserServiceImplTest {@Autowiredprivate IUserService userService;@Testpublic void sendCode() {String code = new Snowflake().nextIdStr().substring(0, 6);System.out.println(code);userService.sendCode(code);}
}
消费者消费验证码
package com.tianju.auth.consumer;import com.tianju.auth.service.IEmailService;
import com.tianju.auth.util.RabbitMqConstants;
import com.tianju.auth.util.SMSUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Slf4j
@Service
public class UserConsumer {@Autowiredprivate IEmailService emailService;@RabbitListener(queues = RabbitMqConstants.MQ_MAIL_QUEUE)public void emailConsumer(String msg){log.debug("[email消费者:]消费{}",msg);emailService.sendEmail("xxxx@qq.com", "登陆验证码", msg);}@RabbitListener(queues = RabbitMqConstants.MQ_PHONE_QUEUE)public void phoneConsumer(String msg){log.debug("[phone消费者:]消费{}",msg);SMSUtil.send("xxxx", msg);}}
topic模式
例如: routingkey: my.orange.rabbit —-> Q1,Q2
配置类增加配置
package com.tianju.auth.util;/*** rabbitmq的常量*/
public interface RabbitMqConstants {String MQ_MAIL_QUEUE="mq_email_queue";String MQ_PHONE_QUEUE="mq_phone_queue";String MQ_FANOUT_EXCHANGE="mq_fanout_exchange";String MQ_TOPIC_EXCHANGE="mq_topic_exchange";String MQ_TOPIC_QUEUE_A = "mq_topic_queue_a";String MQ_TOPIC_QUEUE_B = "mq_topic_queue_b";// 参数 String name, boolean durable, boolean exclusive, boolean autoDeleteboolean durable = true;boolean exclusive = false;boolean autoDelete = false;}
package com.tianju.auth.config;import com.tianju.auth.util.RabbitMqConstants;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitMqConfig {@Bean // 邮箱的队列public Queue mailQueue(){return new Queue(RabbitMqConstants.MQ_MAIL_QUEUE,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete);}@Bean // 电话的队列public Queue phoneQueue(){return new Queue(RabbitMqConstants.MQ_PHONE_QUEUE,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete);}@Bean // 交换机public FanoutExchange fanoutExchange(){return new FanoutExchange(RabbitMqConstants.MQ_FANOUT_EXCHANGE,RabbitMqConstants.durable,RabbitMqConstants.autoDelete);}@Beanpublic Binding mailBinding(){return BindingBuilder.bind(mailQueue()).to(fanoutExchange());}@Beanpublic Binding phoneBinding(){return BindingBuilder.bind(phoneQueue()).to(fanoutExchange());}@Bean // A队列public Queue topicAQueue(){return new Queue(RabbitMqConstants.MQ_TOPIC_QUEUE_A,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete);}/*** topic模式相关配置*/@Bean // B队列public Queue topicBQueue(){return new Queue(RabbitMqConstants.MQ_TOPIC_QUEUE_B,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete);}@Bean // topic的交换机public TopicExchange topicMyExchange(){return new TopicExchange(RabbitMqConstants.MQ_TOPIC_EXCHANGE,RabbitMqConstants.durable,RabbitMqConstants.autoDelete);}@Beanpublic Binding topicAQueueBinding(){return BindingBuilder.bind(topicAQueue()).to(topicMyExchange()).with("topic.xxx"); // 规则 topic.xxx}@Beanpublic Binding topicBQueueBinding(){return BindingBuilder.bind(topicBQueue()).to(topicMyExchange()).with("topic.*"); // 规则 topic.xxx}}
生产者发送信息
/*** topic模式下,生产者发送信息给交换机,可以决定给哪个队列发信息* @param msg 发送的信息* @param routingKey 类似正则表达式,决定给谁发* .with("topic.xxx"); // 规则 topic.xxx ---- A队列* .with("topic.*"); // 规则 topic.xxx ---- B队列* 在配置类中,如上所述配置,则如果输入的routingKey为 topic.xxx则给A和B发;* 如果输入的routingKey为 topic.yyy 则 只给B队列发;*/void sendMsg(String msg,String routingKey);
实现
package com.tianju.auth.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianju.auth.entity.UserPrivs;
import com.tianju.auth.mapper.UserMapper;
import com.tianju.auth.service.IUserService;
import com.tianju.auth.util.RabbitMqConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
@Slf4j
public class UserServiceImpl implements IUserService {@Autowiredprivate RabbitTemplate rabbitTemplate;@Overridepublic void sendCode(String msg) {rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_FANOUT_EXCHANGE,"routingkey.fanout",msg);log.debug("[生产者向交换机:] 发送一条信息:{}",msg);}@Overridepublic void sendMsg(String msg,String routingKey) {rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_TOPIC_EXCHANGE,routingKey, // "topic.yyy",此时只有B队列有信息msg);log.debug("[生产者向交换机:] 发送一条信息:{}",msg);}}
进行发送
package com.tianju.auth.service.impl;import cn.hutool.core.lang.Snowflake;
import com.tianju.auth.service.IUserService;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)public class UserServiceImplTest {@Autowiredprivate IUserService userService;@Testpublic void sendCode() {String code = new Snowflake().nextIdStr().substring(0, 6);System.out.println(code);userService.sendCode(code);}@Testpublic void sendTopic() {String code = new Snowflake().nextIdStr().substring(0, 6);System.out.println(code);userService.sendMsg(code,"topic.yyy");}
}
控制台查看
rabbitmq回调确认
配置类
spring:# rabbitmq的配置rabbitmq:host: 192.168.111.130port: 5672username: adminpassword: 123# 确认收到publisher-confirm-type: correlatedpublisher-returns: true
验证生产者发送是否成功
使用RabbitTemplate的回调方法。
先设置
- setConfirmCallback
- setReturnsCallback
@Autowiredprivate RabbitTemplate rabbitTemplate;@Overridepublic void sendCode(String msg) {rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_FANOUT_EXCHANGE,"routingkey.fanout",msg);log.debug("[生产者向交换机:] 发送一条信息:{}",msg);}@Overridepublic void sendMsg(String msg,String routingKey) {// 如果发到交换机,看一下有没有反馈rabbitTemplate.setConfirmCallback((c,ack,message)->{log.debug("***** setConfirmCallback:ack--{}", ack); // 是否发送到交换机log.debug("***** setConfirmCallback:c-->{}",c);// channel error; protocol method: #method<channel.close>(reply-code=404,// reply-text=NOT_FOUND - no exchange 'aaaa' in vhost '/', class-id=60, method-id=40)log.debug("***** setConfirmCallback:m-->{}",message);if (ack){log.debug("[生产者:] 发送信息到交换机{}","RabbitMqConstants.MQ_TOPIC_EXCHANGE");}else {log.debug(message);}});rabbitTemplate.setReturnsCallback(r->{log.debug("返回文字{}", r.getReplyText());log.debug("返回code{}", r.getReplyCode());log.debug("返回Exchange{}", r.getExchange());log.debug("返回RoutingKey{}", r.getRoutingKey());});rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_TOPIC_EXCHANGE,
// "aaaa",// 失败的情况routingKey, // "topic.yyy",此时只有B队列有信息msg);log.debug("[生产者向交换机:] 发送一条信息:{}",msg);}
rabbitTemplate.setConfirmCallback((c,ack,message)->{log.debug("******* setConfirmCallback:ack->{}",ack);log.debug("******* setConfirmCallback:c->{}",c);log.debug("******* setConfirmCallback:chanel->{}",message);if(ack){log.debug("[生产者]发送信息到达交换机{}","RabbitMqConstants.MQ_TOPIC_EXCHANGE");}else {log.debug(message);}
});
rabbitTemplate.setReturnsCallback(r->{log.debug("返回文字:{}",r.getReplyText());log.debug("返回code:{}",r.getReplyCode());log.debug("返回Exchange:{}",r.getExchange());log.debug("返回RoutingKey:{}",r.getRoutingKey());
});
rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_TOPIC_EXCHANGE,"abc.xxx",msg
);
@Testpublic void sendTopic() {String code = new Snowflake().nextIdStr().substring(0, 6);System.out.println(code);userService.sendMsg(code,"topic.rrr");}
延迟队列(死信)设计
Documentation: Table of Contents — RabbitMQ
java代码步骤
创建正常+死信队列
package com.tianju.mq.config;import com.tianju.mq.constants.RabbitMqConstants;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;
import java.util.Map;@Configuration
public class RabbitMqConfig {@Beanpublic DirectExchange normalExchange(){return new DirectExchange(RabbitMqConstants.MQ_NORMAL_EXCHANGE,RabbitMqConstants.durable,RabbitMqConstants.autoDelete);}@Beanpublic Queue normalQueue(){Map<String, Object> map = new HashMap<>(2);map.put("x-dead-letter-exchange",RabbitMqConstants.MQ_DELAY_EXCHANGE);map.put("x-dead-letter-routing-key",RabbitMqConstants.MQ_DELAY_ROUTING_KEY);return new Queue(RabbitMqConstants.MQ_NORMAL_QUEUE,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete,map);}@Beanpublic Binding normalBinding(){return BindingBuilder.bind(normalQueue()).to(normalExchange()).with(RabbitMqConstants.MQ_NORMAL_ROUTING_KEY);}//------------------死信队列设计--------------------------/*** 死信(延迟)队列* @return*/@Beanpublic Queue delayQueue(){return new Queue(RabbitMqConstants.MQ_DELAY_QUEUE,RabbitMqConstants.durable,RabbitMqConstants.exclusive,RabbitMqConstants.autoDelete);}/*** 死信交换机* @return*/@Beanpublic DirectExchange delayExchange(){return new DirectExchange(RabbitMqConstants.MQ_DELAY_EXCHANGE,RabbitMqConstants.durable,RabbitMqConstants.autoDelete);}/*** 死信交换机队列绑定* @return*/@Beanpublic Binding delayBinding(){return BindingBuilder.bind(delayQueue()).to(delayExchange()).with(RabbitMqConstants.MQ_DELAY_ROUTING_KEY);}
}
配置类+常量
package com.tianju.mq.constants;public interface RabbitMqConstants {String MQ_DELAY_QUEUE = "mq_delay_queue"; // 延迟队列,死信队列String MQ_DELAY_EXCHANGE = "mq_delay_exchange"; // 死信交换机String MQ_DELAY_ROUTING_KEY = "mq_delay_routing_key"; // 死信路由// 正常的队列,交换机,路由String MQ_NORMAL_QUEUE = "mq_normal_queue";String MQ_NORMAL_EXCHANGE = "mq_normal_exchange";String MQ_NORMAL_ROUTING_KEY = "mq_normal_routing_key";// 参数boolean durable = true;boolean exclusive = false;boolean autoDelete = false;
}
server:port: 9099spring:# 邮箱的配置mail:host: smtp.qq.comport: 587username: xxxxx.compassword: xxxxx# rabbitmq的配置rabbitmq:host: 192.168.111.130port: 5672username: adminpassword: 123# 确认收到publisher-confirm-type: correlatedpublisher-returns: truelogging:level:com.tianju.mq: debug
生产者到正常队列
package com.tianju.mq.service;public interface IUserService {/*** 延迟队列的生产者* @param msg 发送的信息* @param delayTime 延迟的时间,毫秒*/void sendDelay(String msg,int delayTime);
}
package com.tianju.mq.service.impl;import com.tianju.mq.constants.RabbitMqConstants;
import com.tianju.mq.service.IUserService;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Date;@Service
@Slf4j
public class UserServiceImpl implements IUserService {@Autowiredprivate RabbitTemplate rabbitTemplate;@Overridepublic void sendDelay(String msg, int delayTime) {rabbitTemplate.convertAndSend(RabbitMqConstants.MQ_NORMAL_EXCHANGE,RabbitMqConstants.MQ_NORMAL_ROUTING_KEY,msg,process->{process.getMessageProperties().setExpiration(String.valueOf(delayTime));return process;});log.debug("[生产者:]发送消息:{},时间{},延迟{}秒",msg,new Date(),delayTime/1000);}
}
消费者进行延迟消费
package com.tianju.mq.consumer;import com.tianju.mq.constants.RabbitMqConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;import java.util.Date;@Service
@Slf4j
public class UserConsumer {@RabbitListener(queues = RabbitMqConstants.MQ_DELAY_QUEUE)public void delayConsume(String msg){log.debug("[消费者消费信息:{},时间:{}",msg,new Date());}
}
延迟队列插件安装
访问官网
Community Plugins — RabbitMQ
进入rabbitmq docker容器
[root@localhost ~]# docker exec -it rabbitmq bash
查询插件列表是否存在延迟插件
root@6d2342d51b11:/plugins# rabbitmq-plugins list
Listing plugins with pattern ".*" ...Configured: E = explicitly enabled; e = implicitly enabled| Status: * = running on rabbit@6d2342d51b11|/
[ ] rabbitmq_amqp1_0 3.9.11
[ ] rabbitmq_auth_backend_cache 3.9.11
[ ] rabbitmq_auth_backend_http 3.9.11
[ ] rabbitmq_auth_backend_ldap 3.9.11
[ ] rabbitmq_auth_backend_oauth2 3.9.11
[ ] rabbitmq_auth_mechanism_ssl 3.9.11
[ ] rabbitmq_consistent_hash_exchange 3.9.11
[ ] rabbitmq_event_exchange 3.9.11
[ ] rabbitmq_federation 3.9.11
[ ] rabbitmq_federation_management 3.9.11
[ ] rabbitmq_jms_topic_exchange 3.9.11
[E*] rabbitmq_management 3.9.11
[e*] rabbitmq_management_agent 3.9.11
[ ] rabbitmq_mqtt 3.9.11
[ ] rabbitmq_peer_discovery_aws 3.9.11
[ ] rabbitmq_peer_discovery_common 3.9.11
[ ] rabbitmq_peer_discovery_consul 3.9.11
[ ] rabbitmq_peer_discovery_etcd 3.9.11
[ ] rabbitmq_peer_discovery_k8s 3.9.11
[E*] rabbitmq_prometheus 3.9.11
[ ] rabbitmq_random_exchange 3.9.11
[ ] rabbitmq_recent_history_exchange 3.9.11
[ ] rabbitmq_sharding 3.9.11
[ ] rabbitmq_shovel 3.9.11
[ ] rabbitmq_shovel_management 3.9.11
[ ] rabbitmq_stomp 3.9.11
[ ] rabbitmq_stream 3.9.11
[ ] rabbitmq_stream_management 3.9.11
[ ] rabbitmq_top 3.9.11
[ ] rabbitmq_tracing 3.9.11
[ ] rabbitmq_trust_store 3.9.11
[e*] rabbitmq_web_dispatch 3.9.11
[ ] rabbitmq_web_mqtt 3.9.11
[ ] rabbitmq_web_mqtt_examples 3.9.11
[ ] rabbitmq_web_stomp 3.9.11
[ ] rabbitmq_web_stomp_examples 3.9.11
下载支持3.9.x的插件
退出容器:
root@6d2342d51b11:/plugins# exit
exit
上传到linux服务器
在/usr/local/software/下创建文件夹rabbitmq/plugins
[root@localhost software]# mkdir -p rabbitmq/plugins
拷贝插件到容器中
[root@localhost plugins]# docker cp ./rabbitmq_delayed_message_exchange-3.9.0.ez rabbitmq:/plugins
进入容器安装插件
[root@localhost plugins]# docker exec -it rabbitmq bash
root@6d2342d51b11:/# rabbitmq-plugins enable rabbitmq_delayed_message_exchange
打开管理页面
进入Exchange页面,下拉Type看是否已经安装成功。
总结
1.rabbitmq队列方式的梳理,点对点,一对多;
2.发布订阅模式,交换机到消费者,以邮箱和手机验证码为例;
3.topic模式,根据规则决定发送给哪个队列;
4.rabbitmq回调确认,setConfirmCallback和setReturnsCallback;
5.死信队列,延迟队列,创建方法,正常—死信,设置延迟时间;
相关文章:

RabbitMQ基础(2)——发布订阅/fanout模式 topic模式 rabbitmq回调确认 延迟队列(死信)设计
目录 引出点对点(simple)Work queues 一对多发布订阅/fanout模式以登陆验证码为例pom文件导包application.yml文件rabbitmq的配置生产者生成验证码,发送给交换机消费者消费验证码 topic模式配置类增加配置生产者发送信息进行发送控制台查看 rabbitmq回调确认配置类验…...
2. VisionOS平台概述
Unity 对VisionOS的支持将 Unity 编辑器和运行时引擎的全部功能与RealityKit提供的渲染功能结合起来。Unity 的核心功能(包括脚本、物理、动画混合、AI、场景管理等)无需修改即可支持。这允许游戏和应用程序逻辑像任何其他 Unity 支持的平台一样在Vision…...

MySql存储过程详解
文章目录 存储过程1 介绍 基本语法创建:调用查看删除演示: 变量相关系统变量演示: 用户自定义变量局部变量 if语法参数介绍casewhilerepeatloop游标条件处理程序存储函数 存储过程 1 介绍 存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过…...
CRM 系统实施风险分析
企业实施 CRM 系统将引起各个方面的巨大变化, CRM 系统实施项目中,有不 少成功的案例,也存在相当的风险,企业只有增强风险意识并积极防范,才有可能提高 CRM 实施成功的概率。 1.企业内部环境带来的风险 (…...

保持城市天际线(力扣)贪心 JAVA
给你一座由 n x n 个街区组成的城市,每个街区都包含一座立方体建筑。给你一个下标从 0 开始的 n x n 整数矩阵 grid ,其中 grid[r][c] 表示坐落于 r 行 c 列的建筑物的 高度 。 城市的 天际线 是从远处观察城市时,所有建筑物形成的外部轮廓。…...

电路综合原理与实践---T衰减与PI衰减的详细计算理论与设计仿真
电路综合原理与实践—T衰减与PI衰减的详细计算理论与设计仿真 最近要找工作在刷笔试题目,会刷到关于T衰减的理论计算问题,一直搞不明白怎么算的,搞明白之后给大家伙来分享一下。 基础理论可以参考:电阻衰减网络计算(P…...

1. 基于UDP的TFTP文件传输
1)tftp协议概述 简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输 特点: 是应用层协议 基于UDP协议实现 数据传输模式 octet:二进制模式(常用) mail:…...

django中使用bootstrap-datepicker时间插件
1、插件的下载 Bootstrap Datepicker是一款基 于Bootstrap框架的日期选择控件,可以方便地在Web应用中添加可交互的日期选择功能。Bootstrap Datepicker拥有丰富的选项和API,支持多种日期格式,可以自定义样式并支持各种语言。 Bootstrap Datepicker 依赖…...
《golang设计模式》第二部分·结构型模式-02-桥接模式(Bridge)
文章目录 1. 概念1.1 角色1.2 类图 2. 代码示例2.1 设计2.1 代码2.2 类图 1. 概念 客户端调用桥接接口实现原有功能和扩展功能的组合 1.1 角色 Implementor(实施者): 具体实施者的抽象,可以是一个接口。 Concrete Implementor&…...

【2023年11月第四版教材】《第4章-信息系统管理之管理要点(第四版新增章节)(第二部分)》
信息系统管理之管理要点(第四版新增章节)(第二部分) 2 管理要点2.1 数据管理能力成熟度评估模型DCMM详细表格简要表格 2.2 组织的管理成熟度2.3 能力模型2.4 智能运维能力框架2.5 安全保护等级 2 管理要点 2.1 数据管理能力成熟度…...

【算法——双指针】LeetCode 1089 复写零
千万不要被这道题标注着“简单”迷惑了,实际上需要注意的细节很多。 题目描述: 解题思路: 正序遍历,确定结果数组的最后一个元素所在的位置;知道最后一个元素的位置后倒序进行填充。 先找到最后一个需要复写的数 先…...

基于飞桨图学习框架实现的城市地点动态关系挖掘
李双利 飞桨开发者技术专家(PPDE),百度研究院商业智能实验室研究实习生,中国科学技术大学在读博士生。 主要进行时空数据挖掘和图深度学习的相关研究工作。曾获2021年百度研究院年度优秀实习生,有多篇基于飞桨完成的…...

3.1 Qt样式选择器
本期内容 3.1 样式选择器 3.1.1 Universal Selector (通用选择器) 3.1.2 Type Selector (类型选择器) 3.1.3 Property Selector (属性选择器) 3.1.4 Class Selector (类选择器) 3.1.5 ID Selector (ID选择器) 3.1.6 Descendant Selector (后裔选择器) 3.1.7 Chil…...
react钩子副作用理解
useEffect(() > { fetch(‘https://api.example.com/data’) .then(response > response.json()) .then(data > setData(data)); }, []); 怎么理解这个[] 在 React 中,useEffect 钩子用于处理副作用,比如数据获取、订阅、手动 DOM 操作等。useE…...

浅谈Spring与字节码生成技术
概要 今天来谈一谈我们熟知的Spring框架和字节码技术有什么联系。 Java程序员几乎都了解Spring。 它的IoC(依赖反转)和AOP(面向切面编程)功能非常强大、易用。而它背后的字节码生成技术(在运行时,根据需要…...

时序预测 | MATLAB实现基于BiLSTM双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)
时序预测 | MATLAB实现基于BiLSTM双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于BiLSTM双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍 Matlab实现BiLST…...

Flink多流处理之coGroup(协同分组)
这篇文章主要介绍协同分组coGroup的使用,先讲解API代码模板,后面会结图解介绍coGroup是如何将流中数据进行分组的. 1 API介绍 数据源# 左流数据 ➜ ~ nc -lk 6666 101,Tom 102,小明 103,小黑 104,张强 105,Ken 106,GG小日子 107,小花 108,赵宣艺 109,明亮# 右流数据 ➜ ~ n…...

基于TICK的DevOps监控实战(Ubuntu20.04系统,Telegraf+InfluDB+Chronograf+Kapacitor)
1、TICK简介 TICK是InfluxData开发的开源高性能时序中台,集成了采集、存储、分析、可视化等能力,由Telegraf, InfluDB, Chronograf, Kapacitor等4个组件以一种灵活松散、但又紧密配合,互为补充的方式构成。TICK专注于DevOps监控、IoT监控、实…...

十九、docker学习-Dockerfile
Dockerfile 官网地址 https://docs.docker.com/engine/reference/builder/Dockerfile其实就是我们用来构建Docker镜像的源码,当然这不是所谓的编程源码,而是一些命令的集合,只要理解它的逻辑和语法格式,就可以很容易的编写Docke…...

Docker容器的数据卷
1.数据卷的概念及作用 2.数据卷的配置 创建容器并挂载数据卷: docker run -it --namec1 -v /root/data:/root/data_container centos:7 /bin/bash按照容器挂载数据卷的原理,data_contianer这个目录下也会同步下来数据的更改。 3.一个容器挂载多个数据…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...