准备工作
安装在安装完rabbitMq后,输入http://ip:15672/ ,是可以看到一个简单后台管理界面的。
RabbitMq简介
常用的交换机有以下三种,因为消费者是从队列获取信息的,队列是绑定交换机的(一般),所以对应的消息推送/接收模式也会有以下几种:
1)Direct Exchange
直连型交换机,根据消息携带的路由键将消息投递给对应队列。
大致流程,有一个队列绑定到一个直连交换机上,同时赋予一个路由键 routing key 。然后当一个消息携带着路由值为X,这个消息通过生产者发送给交换机时,交换机就会根据这个路由值X去寻找绑定值也是X的队列。
2)Fanout Exchange
扇型交换机,这个交换机没有路由键概念,就算你绑了路由键也是无视的。 这个交换机在接收到消息后,会直接转发到绑定到它上面的所有队列。
3)Topic Exchange
主题交换机,这个交换机其实跟直连交换机流程差不多,但是它的特点就是在它的路由键和绑定键之间是有规则的。
简单地介绍下规则
(*号) 用来表示一个单词 (必须出现的)
(#号) 用来表示任意数量(零个或多个)单词
通配的绑定键是跟队列进行绑定的,举个小例子
队列Q1 绑定键为 .TT. 队列Q2绑定键为 TT.#
如果一条消息携带的路由键为 A.TT.B,那么队列Q1将会收到;
如果一条消息携带的路由键为TT.AA.BB,那么队列Q2将会收到
3.下面写了一个以直型交换机为的demo里面有消息确认,失败消息重发机制
创建两个springboot工程 一个为提供者 一个为消费者
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
rabbitmq:
username: guest
port: 5672
password: guest
host: localhost
virtual-host: /
#开启消息确认模式
publisher-returns: true
publisher-confirms: true
template:
# 消息发送失败返回到队列中, yml需要配置 publisher-returns: true
mandatory: true
listener:
simple:
acknowledge-mode: manual #手动ACK
default-requeue-rejected: false #个字段一定要设置成 false 不然无法消费的数据不会进入死信队列的
concurrency: 1 #同一个队列启动几个消费者
max-concurrency: 1 #启动消费者最大数量
prefetch: 1 #限制每次发送一条数据
retry:
enabled: true #是否支持重试
@Component
public class ReceiveHandler {
@RabbitListener(queues = {
RabbitmqConfig.QUEUE_INFORM_EMAIL})
public void send_email(Message message, Channel channel) throws IOException {
// 采用手动应答模式, 手动确认应答更为安全稳定
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
System.out.println("receive message is:"+ new String(message.getBody(),"utf-8"));
}
}
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
//声明队列
public static final String QUEUE_INFORM_EMAIL = "email_queue";
public static final String QUEUE_INFORM_SMS = "sms_queue";
//直型交换机名字
public static final String EXCHANGE_DECLARE_INFORM="xuecheng_exchange";
//路由键
public static final String ROUTINGKEY_EMAIL="inform.email";
public static final String ROUTINGKEY_SMS="inform.sms";
//声明 直型交换机
@Bean(EXCHANGE_DECLARE_INFORM)
public Exchange EXCHANGE_TOPICS_INFORM(){
return new DirectExchange(EXCHANGE_DECLARE_INFORM);
}
//声明 队列
//声明 QUEUE_INFORM_EMAIL队列
@Bean(QUEUE_INFORM_EMAIL)
public Queue QUEUE_INFORM_EMAIL(){
return new Queue(QUEUE_INFORM_EMAIL,true); //true 是否持久
}
//声明 QUEUE_INFORM_SMS队列
@Bean(QUEUE_INFORM_SMS)
public Queue QUEUE_INFORM_SMS(){
return new Queue(QUEUE_INFORM_SMS,true); //true 是否持久
}
//绑定队列
//ROUTINGKEY_SMS队列绑定交换机,指定routingKey
@Bean
public Binding BINDING_ROUTINGKEY_SMS(@Qualifier(QUEUE_INFORM_SMS) Queue queue,
@Qualifier(EXCHANGE_DECLARE_INFORM) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_SMS).noargs();
}
// QUEUE_INFORM_EMAIL队列绑定交换机,指定routingKey
@Bean
public Binding BINDING_ROUTINGKEY_EMAIL(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue,
@Qualifier(EXCHANGE_DECLARE_INFORM) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_EMAIL).noargs();
}
}
4.2 创建发送消息工具类,里面有消息确认,失败消息重发
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.amqp.core.MessageProperties;
@Component
public class RabbitSend implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback{
private RabbitTemplate rabbitTemplate;
@Autowired
public RabbitSend(RabbitTemplate rabbitTemplate) {
super();
this.rabbitTemplate = rabbitTemplate;
this.rabbitTemplate.setMandatory(true);
this.rabbitTemplate.setReturnCallback(this);
this.rabbitTemplate.setConfirmCallback(this);
}
/**
* 发布消息
* @param
*/
public void routeSend(String message,String routingKey) {
Message messages = this.setMessage(message);
//在fanoutExchange中在绑定Q到X上时,会自动把Q的名字当作bindingKey。
rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_DECLARE_INFORM,routingKey,messages);
}
/**
* 设置消息参数
* @param json
* @return
*/
private Message setMessage(String json){
MessageProperties messageProperties = new MessageProperties();
Message message = new Message(json.getBytes(), messageProperties);
//消息持久化
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
}
// 消息确认
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
System.out.println("消息发送确认成功==========");
} else {
System.out.println("消息发送失败=========="+ cause);
}
}
//消息发送失败回传
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("return--message:" + new String(message.getBody()) + ",replyCode:" + replyCode + ",replyText:"
+ replyText + ",exchange:" + exchange + ",routingKey:" + routingKey);
try {
Thread.sleep(10000L);
// TODO 重新发送消息至队列,此处应写一套重发机制,重发多少次结束,否则如果消息如果一直发送失败,则会一直发下去!
this.rabbitTemplate.convertAndSend(exchange, routingKey, message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@RestController
@RequestMapping("/rabitmq")
public class testController {
@Autowired
private RabbitSend rabbitSend;
@GetMapping("/t")
public void t(){
Map<String,Object> map = new HashMap<>();
map.put("msg","这是第一个消息");
map.put("data", Arrays.asList("helloworld",123,true));
rabbitSend.routeSend(map.toString(),RabbitmqConfig.ROUTINGKEY_EMAIL);
}
}
访问 http://localhost:11111/rabitmq/t
消费者打印日志为:
生产者打印日志为
则成功
(源码:https://gitee.com/zhu_can_admin/rabbitmq.git)