首先,配置一個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 表達式,就可以獲取對應的值,動態監聽消費者