使用spring boot整合RabbitMQ,构建多模块工程学习rabbitMQ使用,学习记录如下:
搭建spring-boot多模块工程:
分别演示,RabbitMQ的工作模式:
第一种:
Direct Exchange
Direct Exchange是RabbitMQ默认的交换机模式,也是最简单的模式,根据key全文匹配去寻找队列。
这种工作模式,使用默认交换机,在生产者在发送消息时,直接指定队列名称,将消息发送到指定的队列中,消费者,在接受消息时,指定监听某一指定的队列。
代码演示如下:
公共初始化代码:
首先,在公共工程common,创建RabbitmqConfig类,此类主要用于项目初始化时,创建必要的bean,比如,创建队列,交换机,交换机绑定队列等。
@Configuration
public class RabbitmqConfig {
@Bean
public Queue Queue() {
return new Queue("hello");
}
}
使用@configation注解,默认初始化是创建里面声明的对象,注意要在,启动类开启扫描该包;@ComponentScan(...)
@SpringBootApplication
@ComponentScan(basePackages={"com.yunda.sb.rmq"})//扫描本项目下的所有类
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication .class, args);
}
}
生产者代码:
在producer工程下,创建测试类:
使用测试类,发送消息,注入RabbitTemplate,使用convertAndSend方法,指定队列名称,和消息内容。也可以发送实体对象,但是实体类需要实现序列化接口。
@SpringBootTest
@RunWith(SpringRunner.class)
public class RMQProducerTest {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void send() {
// User user = new User("张三",18 );
// this.rabbitTemplate.convertAndSend("hello", user);
String context = "hello " + new Date();
System.out.println("Sender : " + context);
//注意第一个参数,队列名称,第二个参数,消息内容
this.rabbitTemplate.convertAndSend("hello", context);
}
}
在consumer工程下,创建类,用于监听队列消息
消费者代码:
注意启动类扫描到该包下,创建该监听类,方法上指定监听的队列名称,
@Component
public class ReceiveHandler {
@RabbitListener(queues = "hello")
public void process(String hello) {
System.out.println("Receiver : " + hello);
}
/* @RabbitListener(queues = "hello")
public void process(User user) {
System.out.println("Receiver : " + user.toString());
}*/
}
另一中写法:一个类监听一个队列的方法。
@Component
@RabbitListener(queues = "hello")
public class ReceiveHandler {
@RabbitHandler
public void process(String hello) {
System.out.println("Receiver : " + hello);
}
}
第二种:fanout
发布订阅模式,广播模式生产者将消息发送给交换机,交换机会把这个消息,发给每一个绑定到该交换机的队列,即一对多
代码演示如下:
公共初始化代码:
公共common,配置类初始化,声明交换机,队列,绑定队列与交换机,注意指定的交换机类型,为FanoutExchange,这里使用创建队列方式,一个声明常量方式,另一种直接在注解,使用字符串作为队列名称,注意,在绑定队列到交换机传入的参数,是FanoutExchange。
@Configuration
public class RabbitmqConfigFanout {
//交换机名称
public static final String EXCHANGE_TOPICS_INFORM="fanoutExchange";
//队列名称
public static final String QUEUE_INFORM_EMAIL = "queue01";
public static final String QUEUE_INFORM_SMS = "queue02";
/**
* 交换机配置
* ExchangeBuilder提供了fanout、direct、topic、header交换机类型的配置
* @return the exchange
*/
@Bean(EXCHANGE_TOPICS_INFORM)
public FanoutExchange EXCHANGE_TOPICS_INFORM() {
//durable(true)持久化,消息队列重启后交换机仍然存在
//注意在这里指定的交换机类型,是fanout
return new FanoutExchange(EXCHANGE_TOPICS_INFORM);
}
//声明队列
@Bean(QUEUE_INFORM_SMS)
public Queue QUEUE_INFORM_SMS() {
Queue queue = new Queue(QUEUE_INFORM_SMS);
return queue;
}
//声明队列
@Bean(QUEUE_INFORM_EMAIL)
public Queue QUEUE_INFORM_EMAIL() {
Queue queue = new Queue(QUEUE_INFORM_EMAIL);
return queue;
}
//声明队列
@Bean("queue03") //队列名称
public Queue QUEUE_03() {
Queue queue = new Queue("queue03");
return queue;
}
/**
* 绑定队列到交换机
* @param queue the queue
* @param exchange the exchange
* @return the binding
*/
@Bean
public Binding bindQueue01(@Qualifier(QUEUE_INFORM_SMS) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) FanoutExchange exchange) {
return BindingBuilder.bind(queue).to(exchange);
}
@Bean
public Binding bindQueue02(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) FanoutExchange exchange) {
return BindingBuilder.bind(queue).to(exchange);
}
@Bean
public Binding bindQueue03(@Qualifier("queue03") Queue queue,
@Qualifier(EXCHANGE_TOPICS_INFORM) FanoutExchange exchange) {
return BindingBuilder.bind(queue).to(exchange);
}
如上图,打开rabbit管理界面,找到创建的交换机,绑定的3个队列如上。
生产者代码:
在producer工程下,的测试类,中加入发送消息的测试方法,注意,这里的发送方法的参数,第一个,交换机名称,第二个路由键,默认给空串,第三个消息的内容。
@Test
public void send2() {
String context = "hi, fanout msg ==========> ";
//注意这里是3个参数,第1个交换机名称,第2个路由键默认为空,第3个为消息内容。
this.rabbitTemplate.convertAndSend("fanoutExchange","", context);
System.out.println("Sender : " + context);
// this.rabbitTemplate.convertAndSend(RabbitmqConfigFanout.EXCHANGE_TOPICS_INFORM,"", context);
}
消费者代码:
在consumer工程下,创建消息监听类,ReceiveHandlerFanout,分别监听三个队列,
@Component
public class ReceiveHandlerFanout {
@RabbitListener(queues = "queue01")
public void receive_queue01( String message){
System.out.println(message);
}
@RabbitListener(queues = "queue02")
public void receive_queue02( String message){
System.out.println(message);
}
@RabbitListener(queues = "queue03")
public void receive_queue03( String message){
System.out.println(message);
}
}
第三种方式:Routing模式
交换机为direct模式,队列绑定交换机,并指定路由键,生产者,发送消息到交换机,并同时给出了路由键,交换机根据给过来的路由键,将消息,放到相应得到队列中去。
代码演示如下:
公共工程common下,创建RabbitmqConfigRouting配置类,初始化创建交换机,队列,队列绑定交换机并指定路由键,代码如下:注意交换机类型为,DirectExchange
@Component
public class RabbitmqConfigRouting {
/**第一步,创建交换机
* 交换机配置
* ExchangeBuilder提供了fanout、direct、topic、header交换机类型的配置
*/
@Bean("routingExchange")
public DirectExchange creatRoutingExchange() {
//durable(true)持久化,消息队列重启后交换机仍然存在
//注意在这里指定的交换机类型,是fanout
return new DirectExchange("routingExchange");
}
/**第二步,创建队列
* 指定队列名称,bean名称
*/
//声明队列
@Bean("queueAA") //队列名称
public Queue QUEUE_AA() {
Queue queue = new Queue("queueAA");
return queue;
}
//声明队列
@Bean("queueBB") //队列名称
public Queue QUEUE_BB() {
Queue queue = new Queue("queueBB");
return queue;
}
/**
* 第三步,队列绑定交换机,并指定绑定规则,即说明绑定的路由规则
* 传入参数,交换机和队列
*/
@Bean
public Binding BINDING_QUEUE_AA(@Qualifier("queueAA") Queue queue,
@Qualifier("routingExchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("AA");
}
@Bean
public Binding BINDING_QUEUE_BB(@Qualifier("queueBB") Queue queue,
@Qualifier("routingExchange") DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("BB");
}
}
启动项目,打开管理页面,发现,已经创建了交换机并绑定了对应的队列如下图,对比上述代码。交换机名称,队列名称,路由键。
生产者代码:
在producer工程的测试类中,继续创建发送消息的方法,如下,指定路由键,
代码如下:
@Test
public void sendAA() {
String context = "hi AA, this is msg to AA queue ";
//注意这里是3个参数,第1个交换机名称,第2个路由键,第3个为消息内容。
this.rabbitTemplate.convertAndSend("routingExchange","AA", context);
System.out.println("Sender : " + context);
}
@Test
public void sendBB() {
String context = "hi BB, this is msg to BB queue ";
//注意这里是3个参数,第1个交换机名称,第2个路由键,第3个为消息内容。
this.rabbitTemplate.convertAndSend("routingExchange","BB", context);
System.out.println("Sender : " + context);
}
消费者代码:
在consumer工程下,创建类ReceiveHandlerRouting,用于监听这个2个队列,
@Component
public class ReceiveHandlerRouting {
@RabbitListener(queues = "queueAA")//监听队列AA
public void receive_queueAA( String message){
System.out.println("Receiver A "+message);
}
@RabbitListener(queues = "queueBB") //监听队列BB
public void receive_queueBB( String message){
System.out.println("Receiver B "+message);
}
}
分别测试,向AA队列,BB队列发送消息,测试成功。
第四种方式 Topics
Topics 话题模式或者主题模式,这种模式下,交换机的类型为topic,队列绑定交换机的时候,不再是一个确定的路由键,而是带通配符的路由模式,生产者在发送消息时,会指定一个具体的路由键,交换机拿到这个路由键后,去匹配跟它绑定队列的路由模式,如果,匹配上这个路由键,就会把这个消息投递到这队列中,主要匹配上就会投递,所有,一条消息,可以投递到多个队列中去,只要这些队列满足匹配规则。
代码演示如下:
公共配置类,在common工程下,创建初始化配置类,RabbitmqConfigTopics,指定交换机类型,创建队列,队列绑定交换机并指定路由模式。代码入下:
@Component
public class RabbitmqConfigTopics {
/**第一步,创建交换机
* 交换机配置
* ExchangeBuilder提供了fanout、direct、topic、header交换机类型的配置
*/
@Bean("topicsExchange")
public TopicExchange createTopExchange() {
//durable(true)持久化,消息队列重启后交换机仍然存在
//注意在这里指定的交换机类型,是topic
return new TopicExchange("topicsExchange");
}
/**第二步,创建队列
* 指定队列名称,bean名称
*/
@Bean("topicA")
public Queue QueueA() {
return new Queue("topicA");
}
@Bean("topicB")
public Queue QueueB() {
return new Queue("topicB");
}
/**
* 第三步,队列绑定交换机,并指定绑定规则,
* 绑定规则为指定路由模式,不是一个固定的路由键
* 传入参数,交换机和队列
*/
@Bean
public Binding BINDING_QUEUE_Topic_A(@Qualifier("topicA") Queue queue,
@Qualifier("topicsExchange") TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("topic.#.A.*");
}
@Bean
public Binding BINDING_QUEUE_Topic_B(@Qualifier("topicB") Queue queue,
@Qualifier("topicsExchange") TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("topic.*.B.#");
}
}
启动项目,打开管理页面,观察创建好的交换机,队列,及路由模式
生产者代码:
在producer工程下的测试类中创建测试方法,注意模式的匹配。
在发消息是,指定具体的路由键,交换机拿到路由键去匹配路由模式,匹配成功,就进行投递消息到队列
//*表示一个词.
// #表示零个或多个词.
@Test
public void sendTopicA() {
String context = "hi TopicA, 这个消息只能由A队列接受 ";
//注意这里是3个参数,第1个交换机名称,第2个路由键,第3个为消息内容 topicA的路由模式,topic.#.A.*
this.rabbitTemplate.convertAndSend("topicsExchange","topic.A.info", context);
System.out.println("Sender : " + context);
}
@Test
public void sendTopicB() {
String context = "hi TopicB, 这个消息只能由B队列接受";
//注意这里是3个参数,第1个交换机名称,第2个路由键,第3个为消息内容。topicB的路由模式,topic.*.B.#
this.rabbitTemplate.convertAndSend("topicsExchange","topic.info.B", context);
System.out.println("Sender : " + context);
}
@Test
public void sendTopicC() {
String context = "hi TopicA and TopicB , 这个消息可以由A队列,B队列接受";
//注意这里是3个参数,第1个交换机名称,第2个路由键,第3个为消息内容。
this.rabbitTemplate.convertAndSend("topicsExchange","topic.A.B", context);
System.out.println("Sender : " + context);
}
消费者代码:
在consumer工程下创建监听类ReceiveHandlerTopics,获取队列消息,
@Component
public class ReceiveHandlerTopics {
@RabbitListener(queues = "topicA")//监听队列topicA
public void receive_queueAA( String message){
System.out.println("Receiver: topicA ,我获取到消息:"+message);
}
@RabbitListener(queues = "topicB") //监听队列topicB
public void receive_queueBB( String message){
System.out.println("Receiver: topicB,我获取到消息:"+message);
}
}
分别发送消息,测试成功。