首先,配置一个spring 线程池
@Slf4j
@Configuration
public class TaskExecutorConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(4);
taskExecutor.setMaxPoolSize(8);
taskExecutor.setQueueCapacity(100);
taskExecutor.setThreadNamePrefix("myExecutor-");
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.initialize();
log.debug("Thread pool initialization.");
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
注意启动线程池@EnableAsync 在*Application.java上加入这个注解
配置文件: application.yaml
spring:
rabbitmq:
host: 192.168.*.* //ip
port: 5672 //端口
username: admin //用户名
password: 123456 //密码
publisher-confirms: true //消息接收确认
publisher-returns: true //消息失败返回
template:
mandatory: true //失败返回需要为true
listener:
direct:
acknowledge-mode: manual //手动 ack
simple:
acknowledge-mode: manual //手动 ack
消息转化器配置: Jackson2JsonMessageConverter,比默认的SerializerMessageConverter强大
@Configuration
public class RabbitMQConfig {
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 必须设置为 true,不然当 发送到交换器成功,但是没有匹配的队列,不会触发 ReturnCallback 回调
// 而且 ReturnCallback 比 ConfirmCallback 先回调,意思就是 ReturnCallback 执行完了才会执行 ConfirmCallback
rabbitTemplate.setMandatory(true);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
@Bean
public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 修改了messageConverter后需要重新设置手动确认
return factory;
}
}
然后,作为消费者的方法上加入注解监听和异步注解
@Async // 异步注解, 相当于多个消费者在监听一个生产者的消息
@RabbitListener(bindings = { // 消费者监听, bindings 绑定交换机和队列
@QueueBinding(
exchange = @Exchange(value = "交换机名字", type = ExchangeTypes.TOPIC), // topic 类型,模糊路由键接收
value = @Queue(value = "队列名字", durable = "true"), // durable = true 持久化
key = "路由键(例: consumer.#)" // 接收consumer.a、consumer.b 等前缀相同的路由键
)
})
// args 接收的参数,对应生产者传递的类型, channel 接口可用来发送消息或确认消息被消费, @Header 获取头信息, tag表示该消息index
public void receiveMsg(Object args, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
//处理args消息信息过程
// 下面是手动消息应答
// channel.basicAck(tag, false); 作用:消息确认接收,参数2是否批量,true表示一次性ack所有小于tag的消息
// channel.basicNack(tag, false, true) 作用:拒绝或重新入队列, 参数2是否批量同上,参数3被拒绝的是否重新入队列
// channel.basicReject(tag, false) 作用:拒绝或重新入队列(只能一次拒绝一条消息),参数2被拒绝的是否重新入队列
}
有一些心得分享一下:有时候只想在一个项目中,监听一个消费者,比如:消费者1,消费者2都是将接收的消息保存到各自的数据库中,那么我们可以在配置文件中指明各自消费者的配置项
spring:
profiles:
active: 消费者1配置文件
消费者1配置文件
spring:
rabbitmq:
routingkey: **.**
receive-queue: 接收队列名1
@RabbitListener(bindings = {
@QueueBinding(
exchange = @Exchange(value = "交换机名字"),
value = @Queue(value = "${spring.rabbitmq.receive-queue}", durable = "true"),
key = "${spring.rabbitmq.routingkey}"
)
})
这样在注解中使用spel 表达式,就可以获取对应的值,动态监听消费者