依赖
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件
application.yml
spring:
rabbitmq:
host: 10.27.10.63
port: 5672
virtual-host: /ems
username: emsuser
password: 123
publisher-confirm-type: simple #开启生产者消息发送确认:生产者到exchange
publisher-returns: true #开启生产者消息发送确认:exchange到queue
listener:
simple:
acknowledge-mode: manual #开启手动应答
retry:
enabled: true #是否支持重试
max-attempts: 2 #最多重试次数
redis:
host: 10.27.20.51
port: 8888
生产者消息发送
序列化方式:
@Configuration
public class RabbitConfig {
// json序列化方式
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
}
@SpringBootTest
class RabbitmqApplicationTests {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void provider(){
rabbitTemplate.convertAndSend("april","人间四月天");
}
// work模型
@Test
public void worker(){
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("work","工作模型:"+i);
}
}
// 广播fanout
@Test
public void fanout(){
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("name","西游记");
stringObjectHashMap.put("number",100);
rabbitTemplate.convertAndSend("logs","",stringObjectHashMap);
}
// 路由direct
@Test
public void direct(){
rabbitTemplate.convertAndSend("directs","info","路由模式");
}
// 发布订阅 topic
@Test
public void topic(){
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("name","西游记");
stringObjectHashMap.put("number",100);
// 较低等级的 send() 方法技巧在于构造要发送的 Message 对象,通过给定字符串的字节数组来构建 Message 实例,
// 对于 String 值来说,这足够了,但是如果消息的负载是复杂对象的话,那它就会复杂得多
// MessageProperties messageProperties = new MessageProperties();
// messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
// // 给消息添加messageId
// messageProperties.setMessageId(UUID.randomUUID().toString());
// // 使用json序列化方式会报错
// Message message = new Message(stringObjectHashMap.toString().getBytes(),messageProperties);
// rabbitTemplate.send("topics","k.save.user",message);
// 鉴于上述这种情况,我们有了 convertAndSend() 方法,它会自动将对象转换为 Message。
// 它需要一个消息转换器的帮助来完成该任务,默认的消息转换器是 SimpleMessageConverter,它适用于 String、Serializable 实例以及字节数组。
// 给消息添加messageId
rabbitTemplate.convertAndSend("topics","k.save.user", stringObjectHashMap.toString(),
message1 -> {
message1.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message1;
},
new CorrelationData(UUID.randomUUID().toString()));
}
}
生产者消息发送确认
@Component
public class PublisherConfirmAndReturnConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {
@Autowired
RabbitTemplate rabbitTemplate;
@PostConstruct// bean创建完成空对象,就开始进行@Autowire、@PostConstruct赋值,也就是在PublisherConfirmAndReturnConfig对象创建完成后会执行此方法
public void init(){
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnsCallback(this);
}
/*
消息发送到exchange确认回调
*/
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
// ack 消息发送到交换机
if(b){
System.out.println("消息被发送到exchange:" + s);
System.out.println("消息被发送到exchange:" + new String(correlationData.getReturned().getMessage().getBody()));
} else { // nack 消息未发送到交换机
System.out.println("消息未被发送到exchange:" + s);
}
}
/*
消息从exchange发送到queue失败回调
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息没被发送到队列:" + returnedMessage.getExchange()+"||"+returnedMessage.getRoutingKey()+"||"+new String(returnedMessage.getMessage().getBody()));
}
}
消费者消息消费
常用注解:@RabbitListener:作用在类上时,消费的方法上添加@RabbitHandler;作用在方法上时,无需添加@RabbitHandler
@RabbitHandler:表示消息消费方法
@Component
// 监听队列 声明的队列默认 持久化 非独占 非自动删除
@RabbitListener(queuesToDeclare = @Queue(value = "april",autoDelete = "false"))
public class consumer1 {
// 消息处理,方法名任意,只需加上@Rabbithandler注解
@RabbitHandler
public void receiver1(String message){
System.out.println(message);
}
}
worktype:
@Component
public class WorkConsumer {
/**
* @RabbitListener 作用在方法上时,方法不再需要 @RabbitHandler 注解
*
*/
@RabbitListener(queuesToDeclare = {@Queue("work")})
public void consumer1(String message){
System.out.println("Consumer1:"+message);
}
@RabbitListener(queuesToDeclare = @Queue("work"))
public void consumer2(String message){
System.out.println("Consumer2:"+message);
}
}
fanout:
@Component
public class FanoutConsumer {
/**
* 绑定临时队列 value = @Queue
* 绑定非临时队列 value = @Queue(value = "logsfanout")
*/
@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "logsfanout"),exchange = @Exchange(value = "logs",type = "fanout")))
public void consumer1(String message){
System.out.println("fanout模式1:"+message);
}
@RabbitListener(bindings = {@QueueBinding(value = @Queue,exchange = @Exchange(value = "logs",type = "fanout"))})
public void consumer2(String message){
System.out.println("fanout模式2:"+message);
}
}
direct:
@Component
public class DirectConsumer {
@RabbitListener(bindings = @QueueBinding(value = @Queue(),exchange = @Exchange(value = "directs",type = "direct"),key = {"warn","error"}))
public void consumer1(String message){
System.out.println("consumer1:"+message);
}
@RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(value = "directs",type = "direct"),key = {"info","error"}))
public void consumer2(String message){
System.out.println("consumer2:"+message);
}
}
topic:
@Component
public class TopicConsumer {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(value = "topics",type = "topic"),key = {"*.user"}))
public void topicConsumer1(String message){
System.out.println("consumer1:"+message);
}
@RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(value = "topics",type = "topic"),key = "#.user.#"))
public void topicConsumer2(String msg, Channel channel, Message message){
// 获取messageId
// 适用方式:messageProperties.setMessageId(UUID.randomUUID().toString());
// String messageId = message.getMessageProperties().getMessageId();
// 适用方式:new CorrelationData(UUID.randomUUID().toString())
String messageId = message.getMessageProperties().getHeader("spring_returned_message_correlation");
System.out.println("messageId:"+messageId);
System.out.println(new String(message.getBody()));
// redis中添加消息数据状态为为消费
Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(messageId, "0", 10, TimeUnit.SECONDS);
try {
// 设置成功:没有被消费过
if(setIfAbsent) {
System.out.println("consumer2:" + msg);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
// 消费完成,改变redis中该消息的消费状态为已消费
stringRedisTemplate.opsForValue().setIfAbsent(messageId, "1", 10, TimeUnit.SECONDS);
} else {// 设置失败:有被发给其他消费者
String status = stringRedisTemplate.opsForValue().get(messageId);
// 消费完成:手动确认;未被消费完成:不做任何操作,给原来的消费者继续消费
if("1".equalsIgnoreCase(status)){
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
} catch (Exception e) {
// 确认失败,发回原队列尾部
try {
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}