上篇文章描述了使用RabbitMQ原生API来实现消息的处理,在实际项目开发中,略显繁琐。我们就可以使用springboot的RabbitMQ插件进行简单配置就可以实现。
一、springboot集成RabbitMQ
1-1、添加依赖
首先创建一个springboot项目,然后添加下面依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
1-2、添加配置信息
spring.rabbitmq.host=192.168.253.131
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
spring.rabbitmq.virtual-host=/mirror
1-3、创建队列及交换机
创建队列及交换机并进行绑定除了在控制台进行操作外,还可以利用程序进行创建,下面来分别介绍一下
1-3-1、使用@Configuration在项目启动时进行创建
使用@Configuration需要注意的是下面声明queue和exchange以及绑定 的方法需要添加@Bean
这种方法创建的好处就是项目启动之后就可以完成创建,缺点是如果想动态创建就无法实现了
绑定交换机和队列使用BindingBuilder.bind(队列).to(交换机)
1-3-1-1、创建fanout交换机
@Configuration
public class FanoutConfig {
//声明队列
@Bean
public Queue fanoutQ1() {
return new Queue("finout.queue1");
}
//声明exchange
@Bean
public FanoutExchange setFanoutExchange() {
return new FanoutExchange("fanout.exchange");
}
//声明Binding,exchange与queue的绑定关系
@Bean
public Binding bindQ1() {
return BindingBuilder.bind(fanoutQ1()).to(setFanoutExchange());
}
}
1-3-1-2、创建topic交换机
绑定交换机和队列使用BindingBuilder.bind(队列).to(交换机).with(路由键)
@Configuration
public class TopicConfig {
//声明队列
@Bean
public Queue topicQ1() {
return new Queue("topic.queue1");
}
//声明exchange
@Bean
public TopicExchange setTopicExchange() {
return new TopicExchange("topic.exchange");
}
//声明binding,需要声明一个roytingKey
@Bean
public Binding bindTopicHebei1() {
return BindingBuilder.bind(topicQ1()).to(setTopicExchange()).with("hunan.*");
}
}
1-3-1-3、创建header交换机
绑定交换机和队列有三种方式如下:
BindingBuilder.bind(队列).to(交换机).where(key).matches(val)
BindingBuilder.bind(队列).to(交换机).whereAny(Map).match()
BindingBuilder.bind(队列).to(交换机).whereAll(Map).match()
实现代码
@Configuration
public class HeaderConfig {
//声明queue
@Bean
public Queue headQueueTxTyp1() {
return new Queue("txTyp1");
}
@Bean
public Queue headQueueBusTyp1() {
return new Queue("busTyp1");
}
@Bean
public Queue headQueueTxBusTyp() {
return new Queue("txbusTyp1");
}
//声明exchange
@Bean
public HeadersExchange setHeaderExchange() {
return new HeadersExchange("headerExchange");
}
//声明Binding
//绑定header中txtyp=1的队列。header的队列匹配可以用mathces和exisits
@Bean
public Binding bindHeaderTxTyp1() {
return BindingBuilder.bind(headQueueTxTyp1()).to(setHeaderExchange()).where("txTyp").matches("1");
}
//绑定Header中busTyp=1的队列。
@Bean
public Binding bindHeaderBusTyp1() {
return BindingBuilder.bind(headQueueBusTyp1()).to(setHeaderExchange()).where("busTyp").matches("1");
}
//绑定Header中txtyp=1或者busTyp=1的队列。
@Bean
public Binding bindHeaderTxBusTyp1() {
Map<String,Object> condMap = new HashMap<>();
condMap.put("txTyp", "1");
condMap.put("busTyp", "1");
return BindingBuilder.bind(headQueueTxBusTyp()).to(setHeaderExchange()).whereAny(condMap).match();
}
}
1-3-2、使用AmqpAdmin
使用AmqpAdmin优点是可以随时进行队列及路由的创建和绑定
需要注意的是,创建交换机需要使用各自类型的交换机进行创建:
new FanoutExchange()
new TopicExchange()
new HeadersExchange()
@Autowired
private AmqpAdmin amqpAdmin;
@GetMapping("initFanoutExchangeQueue")
public Object initQueue(){
String queueName="fanout.queue1";
String exchangeName="fanout.exchange";
//创建队列
Queue queue = new Queue(queueName, false, false, false, null);
amqpAdmin.declareQueue(queue);
//创建交换机
FanoutExchange fanoutExchange = new FanoutExchange("fanout.exchange", false, false, null);
amqpAdmin.declareExchange(fanoutExchange);
//绑定
amqpAdmin.declareBinding(new Binding(queueName, Binding.DestinationType.QUEUE,exchangeName,"",null));
return "success";
}
1-4、发送消息
RabbitMQ使用起来和redis很类似,redis使用RedisTemplate,RabbitMQ则使用RabbitTemplate,下面来看下如何使用
在springboot中使用RabbitTemplate无法发送stream队列消息,因为发送stream队列消息,需要设置basicQos属性,而basicQos属性需要通过channel来设置,RabbitTemplate已经进行封装,使用的时候无法获取channel,因此无法发送stream类型队列消息;
1-4-2、给指定队列发送消息
首先设置消息的相关参数,最后调用rabbitTemplate.send(队列名称,消息内容,请求参数)进行发送
String message="hello word";
//设置部分请求参数
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
//发消息
rabbitTemplate.send("directqueue",new Message(message.getBytes("UTF-8"),messageProperties));
1-4-3、使用exchange的fanout发送消息
向交换机发送消息和向队列发送类似
第一步:设置消息相关参数
MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
第二步:发送消息
rabbitTemplate.send(队列名称,路由名称,消息,消息相关参数)
String message="fanoutmessage";
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
//fanout模式只往exchange里发送消息。分发到exchange下的所有queue
rabbitTemplate.send(MyConstants.EXCHANGE_FANOUT, "", new Message(message.getBytes("UTF-8"),messageProperties));
1-4-4、使用exchange的topic发送消息
String routingKey="routingkey";
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
//发送消息
rabbitTemplate.send("topicExchange", routingKey, new Message(message.getBytes("UTF-8"),messageProperties));
1-4-5、使用exchange的header发送消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
//设置header信息
messageProperties.setHeader("name", "admin");
messageProperties.setHeader("pass", "123");
//发送消息
rabbitTemplate.send("headerExchange", "uselessRoutingKey", new Message(message.getBytes("UTF-8"),messageProperties));
1-4-6、使用quorum队列发送消息
//设置部分请求参数
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
//发消息
rabbitTemplate.send("QUEUE_QUORUM",new Message(message.getBytes("UTF-8"),messageProperties));
1-5、接收消息
消费者都是通过@RabbitListener注解来声明。注解中包含了声明消费者队列时所需要的重点参数。对照原生API,这些参数就不难理解了。
但是当要消费Stream队列时,还是要重点注意他的三个必要的步骤:
- channel必须设置basicQos属性。 channel对象可以在@RabbitListener声明的消费者方法中直接引用,Spring框架会进行注入。
- 正确声明Stream队列。 通过往Spring容器中注入Queue对象的方式声明队列。在Queue对象中传入声明Stream队列所需要的参数。
- 消费时需要指定offset。 可以通过注入Channel对象,使用原生API传入offset属性。
使用SpringBoot框架集成RabbitMQ后,开发过程可以得到很大的简化,所以使用过程并不难,对照一下示例就能很快上手。但是,需要理解一下的是,SpringBoot集成后的RabbitMQ中的很多概念,虽然都能跟原生API对应上,但是这些模型中间都是做了转换的,比如Message,就不是原生RabbitMQ中的消息了。使用SpringBoot框架,尤其需要加深对RabbitMQ原生API的理解,这样才能以不变应万变,深入理解各种看起来简单,但是其实坑很多的各种对象声明方式。
主要通过给方法加上如下注解,并设置队列即可消费对应的消息
@RabbitListener(queues=MyConstants.QUEUE_Name)
1-5-1、接收消息
接收消息的方式,不论是直接接收发送到队列的,还是发送到exchange交换机,方式都是一样,主要区别就是绑定交换机的队列,根据绑定的队列不同而接收对应的消息
@RabbitListener(queues=MyConstants.QUEUE_Name)
public void directReceive2(String message) {
System.out.println("consumer2 received message : " +message);
}
1-5-2、普通队列消息
发送消息,第一个参数为队列名称,如果有多个消费此队列的消费端,只有一个消费端可以进行消费
rabbitTemplate.send("directqueue",new Message(message.getBytes("UTF-8"),messageProperties));
接收消息注解
@RabbitListener(queues="directqueue")
1-5-3、交换机fanout类型接收消息
发送消息,第一个参数为交换机名称,和交换机绑定的队列都可以消费消息
rabbitTemplate.send("EXCHANGE_FANOUT", routingkey, new Message(message.getBytes("UTF-8"),messageProperties));
接收消息,传入和上面EXCHANGE_FANOUT交换机绑定的队列,即可消费消息
@RabbitListener(queues="fanout_queue1")
1-5-4、交换机topic类型接收消息
发送消息,指定一个topic类型的交换机,并且设置routingkey
rabbitTemplate.send("topicExchange", routingKey, new Message(message.getBytes("UTF-8"),messageProperties));
接收消息:
注意这个模式会有优先匹配原则。例如发送routingKey=beijing.haidian,那匹配到beijing.* (beijing.haidian,beijing.chaoyang),之后就不会再去匹配*.haidian(XXX.haidian)
@RabbitListener(queues="beijing.haidian")
1-5-5、交换机header类型接收消息
如下图三个队列绑定了headerExchange交换机
busType1:当header中有busTyp=1则接收消息,即使设置其他key-val也可以
txType1:当header中有txTyp=1则接收消息,即使设置其他key-val也可以
txbusType1:当header中有busTyp=1或者txTyp:1则可以接收消息(因为设置的x-match:any,如果是all则两个都必须满足)