rabbitMq
1,新建exchange 新建queue 在exchange中绑定queue并设置routing_key 2,消息发送,发送到exchange,routingKey 不同的queue会存放自己绑定routingkey的消息 消息监听,监听queue 3,各个消费者监听自己的queue, 每个queue可以存放同样的消息,就是广播模式了 4, exchange类型有: direct直连类型 在exchange中queue需要一一逐个绑定routingKey test.aa test.bb topic主题类型 queue只需要模糊匹配绑定就行 test.#
1,多个服务器实例,多个消费者,每个消费者又可以多线程,并发消费同一队列 2,多个监听消费者,不同队列,同一条数据每个消费者都会消费到,广播 (消费发送到exchange交换机、routingkey上,不同的queue会存放自己绑定routingkey的消息) fanout 可由direct替代,它是一种广播式的交换器(散开分列),表示分发,所有的消费者得到同样的队列信息,发布/订阅,会将接收到的消息发送给所有绑定到该交换器的队列, 而不考虑路由键(routing key)。这意味着无论消息发布时指定了什么路由键,Fanout 交换器都会忽略这些信息,并简单地将消息复制并分发给所有的绑定队列。 direct可以建多个queue监听同一个routingkey来实现分发 可来替代fanout
concurrency 多线程消费
Purge 队列清理
@Autowired
private RabbitTemplate rabbitTemplate;
public String mq(String name) {
log.info("消息生产:{}", name);
rabbitTemplate.convertAndSend("queuexmh", name);
/**
* one_q one_x 映射到 queue_x1, two_q映射到 queue_x2
*/
rabbitTemplate.convertAndSend("exchange_direct_xmh","one_q", "queue_x1"+name);
rabbitTemplate.convertAndSend("exchange_direct_xmh","one_x", "queue_x1"+name);
rabbitTemplate.convertAndSend("exchange_direct_xmh","two_q", "queue_x2"+name);
/**
* one.t1 映射到 queue_x1, two.t1 two.t2 映射到 queue_x2
* 后台做了关联 two.# 映射到 queue_x2, one.# 映射到 queue_x1
*/
rabbitTemplate.convertAndSend("exchange_topic_xmh","two.t1", "queue_x2"+name);
rabbitTemplate.convertAndSend("exchange_topic_xmh","two.t2", "queue_x2"+name);
rabbitTemplate.convertAndSend("exchange_topic_xmh","one.t1", "queue_x1"+name);
return "hello"+name;
}
@Component
public class RabbitConsumer {
//指定queue消费
@RabbitListener(queues = "queue_x1",concurrency = "1")
public void queueX1(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {
try {
System.out.println("queue_x1收到消息:" + message);
//设置为手动确认时生效 手动确认消息(第二个参数false表示不批量确认)
//channel.basicAck(deliveryTag, false);
} catch (Exception e) {
// 处理失败时拒绝消息(requeue=false表示不重回队列)
channel.basicNack(deliveryTag, false, false);
}
}
//指定queue消费
@RabbitListener(queues = "queue_x2",concurrency = "1")
public void queueX2(Message message) {
System.out.println("queue_x2收到消息:" + message);
}
//指定queue消费
@RabbitListener(queues = "${rabbitmq.queue.sendSms}",concurrency = "5",containerFactory =
"rabbitListenerContainerFactory")
public void onMessage(Message message) {
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.6.8</version>
</dependency>
spring:
rabbitmq:
port: 5672
host: 192.168.243.128:5672
username: admin
password: admin
virtual-host: xmh
publisher-confirm-type: correlated
publisher-returns: true
listener:
direct:
acknowledge-mode: auto
simple:
acknowledge-mode: auto
retry:
enabled: true
@Configuration
public class RabbitConfig {
@Value("${spring.rabbitmq.host}")
private String addresses;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
// 构建mq实例工厂
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(addresses);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
return connectionFactory;
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
// 若设置为手动确认,必须手动调用ack AUTO 自动确认 默认值
factory.setAcknowledgeMode(AcknowledgeMode.AUTO);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
return factory;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
return new RabbitAdmin(connectionFactory);
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //每次注入的时候回自动创建一个新的bean实例
public RabbitTemplate rabbitTemplate(){
return new RabbitTemplate(connectionFactory());
}
自动绑定
package com.xmh.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
// 交换机名称
public static final String EXCHANGE_NAME = "xmh.exchange";
// 队列名称
public static final String QUEUE_NAME = "xmh.queue";
public static final String QUEUE_NAME2 = "xmh2.queue";
// 路由键
public static final String ROUTING_KEY = "xmh.routing.key";
public static final String ROUTING_KEY2 = "xmh2.routing.key";
/**
* 声明交换机
* 这里使用的是Direct类型交换机,也可以根据需要使用Topic、Fanout等类型
*/
@Bean
public DirectExchange exampleExchange() {
// durable: 是否持久化 autoDelete: 是否自动删除
return new DirectExchange(EXCHANGE_NAME, true, false);
}
/**
* 声明队列
*/
@Bean
public Queue exampleQueue() {
// durable: 是否持久化 exclusive: 是否排他 autoDelete: 是否自动删除
return QueueBuilder.durable(QUEUE_NAME).build();
}
@Bean
public Queue exampleQueue2() {
// durable: 是否持久化 exclusive: 是否排他 autoDelete: 是否自动删除
return QueueBuilder.durable(QUEUE_NAME2).build();
}
/**
* 绑定交换机和队列
*/
@Bean
public Binding binding(Queue exampleQueue, DirectExchange exampleExchange) {
// 将队列通过路由键绑定到交换机
return BindingBuilder.bind(exampleQueue).to(exampleExchange).with(ROUTING_KEY);
}
@Bean
public Binding binding2(Queue exampleQueue2, DirectExchange exampleExchange) {
// 将队列通过路由键绑定到交换机
return BindingBuilder.bind(exampleQueue2).to(exampleExchange).with(ROUTING_KEY2);
}
}
rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING_KEY2, ROUTING_KEY2+name);
//指定queue消费
@SneakyThrows
@RabbitListener(queues = QUEUE_NAME2,concurrency = "1")
public void queueX2(Message message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
String msg = new String(message.getBody(),"UTF-8");
System.out.println(QUEUE_NAME2+"收到消息:"+msg+":"+ message);
}
RabbitMq 消费失败,重试机制
自定义异常业务处理
步骤1 设置自动确认,自定义表记录失败数据(包含业务数据、失败次数)触发定时任务重新投放该消息,执行3次,每次间隔30分钟
执行成功,删除重发任务
执行失败,第一次失败执行步骤1,否则记录失败次数,达到一定次数删除定时任务
channel.basicReject(deliveryTag, true);// 拒绝消息并重回队列
安装rabbitmq
https://www.rabbitmq.com/release-information rabbitmq erlang CentOS tar -zxf otp_src_28.0.2.tar.gz 修改 Centos 镜像地址 参考 https://www.cnblogs.com/xingminghui111/articles/17514510.html sudo yum install -y ncurses-devel sudo yum install -y gcc gcc-c++ openssl-devel libssh-devel unixODBC-devel ./configure && make && make install erl -version rpm -i --nodeps rabbitmq-server-4.0.9-1.el8.noarch.rpm service rabbitmq-server start service rabbitmq-server status 查看服务状态 配置管理员账号: rabbitmqctl add_user admin admin rabbitmqctl set_user_tags admin administrator # 设置用户为管理员 # 授予所有权限 rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" # 启用管理插件 rabbitmq-plugins enable rabbitmq_management # 重启服务使配置生效 systemctl restart rabbitmq-server 关闭防火墙 sudo systemctl stop firewalld sudo systemctl disable firewalld 后台管理 http://192.168.243.128:15672/
# 停止服务 自启动
systemctl stop rabbitmq-server sudo systemctl enable rabbitmq-server
其他
@Service
public class OrderItemServiceImpl {
/**
* https://blog.csdn.net/aetawt/article/details/128957417
* 监听方式获取消息 消息只消费一次,其他消费者消费后,本消费者不再消费
* @RabbitHandler:标注在方法上
* @RabbitListener: 标注在类、方法上
* 使用 @RabbitHandler + @RabbitListener 接受不同类型的消息
*/
@RabbitListener(queues = {MqConstant.SEARCH_DATA_QUEUE})
public void recieveOrderMessage(Message message, Channel channel) throws IOException {
System.out.println("收到了消息了--->" + message + " ====》内容:" + new String(message.getBody()));
System.out.println("渠道数量:" + channel.getChannelNumber());
MessageProperties messageProperties = message.getMessageProperties();
System.out.println("消息处理完成---------------------------------");
//消息顺序,自增
long deliveryTag = messageProperties.getDeliveryTag();
System.out.println(deliveryTag);
//回复,签收消息, fasle表示只签收当前消息,true签收所有
channel.basicAck(deliveryTag, false);
}
/**
* RabbitTemplate 拉取的方式获取消息
*/
@Component
public class RabbitListenerConfig {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void receive(){
Thread thread = new Thread(){
public void run(){
while (true){
System.out.println("*************ready");
String message = (String)rabbitTemplate.receiveAndConvert(MqConstant.SEARCH_DATA_QUEUE,1000*2);
System.out.println("*************receive:"+message);
}
}
};
thread.start();
}
自定义多线程处理
@RabbitListener(queues = {MqConstant.DEAL_TOBE_HANDLE})//,containerFactory = "batchQueueRabbitListenerContainerFactory"
@Bean("batchQueueRabbitListenerContainerFactory")
public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setMessageConverter(new Jackson2JsonMessageConverter());
//确认方式,manual为手动ack.
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//每次处理数据数量,提高并发量
//factory.setPrefetchCount(250);
//设置线程数
//factory.setConcurrentConsumers(30);
//最大线程数
//factory.setMaxConcurrentConsumers(50);
/* setConnectionFactory:设置spring-amqp的ConnectionFactory。 */
factory.setConnectionFactory(connectionFactory);
factory.setConcurrentConsumers(1);
factory.setPrefetchCount(1);
//factory.setDefaultRequeueRejected(true);
//使用自定义线程池来启动消费者。
factory.setTaskExecutor(taskExecutor());
return factory;
}
@Bean("correctTaskExecutor")
@Primary
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(100);
// 设置最大线程数
executor.setMaxPoolSize(100);
// 设置队列容量
executor.setQueueCapacity(0);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(300);
// 设置默认线程名称
executor.setThreadNamePrefix("thread-rabbitmq");
// 设置拒绝策略rejection-policy:当pool已经达到max size的时候, 调用者执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}

浙公网安备 33010602011771号