SpringBoot整合RabbitMQ

Direct模式

通过routingKey和exchange决定的那个唯一的queue可以接收消息

即消息被发送到指定的消息队列中,然后被接收。

实验步骤概述:

  • 引入mq相关的依赖
  • 添加配置信息(MQ是个网络组建,需要IP、端口号等)
  • 编写配置类,创建MQ
  • 编写发送端代码、接收端代码
  • 编写测试类

pom

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

application.yml

spring:
  rabbitmq:
    host: 192.168.16.128
    port: 5672
    username: guest
    password: guest

注意,代码中使用5672端口,之前的15672是管理端用的。

配置类MqConfig

package com.ah.mq.direct;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.*;

@Configuration
public class DirectConfig {
	static final String MQ_NAME = "direct";

	@Bean
	public Queue createQueue() {
		// 创建一个命名消息队列
		return new Queue(MQ_NAME);
	}
}

发送端、接收端写一起:

发送端使用Controller

接收端是自动接收,没必要用控制器,使用的是@Component

package com.ah.mq.direct;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

@RestController
public class DirectSendRecv {

	@Autowired
	private AmqpTemplate mq;

	public void send() {
		for (int i = 0; i < 10; i++) {
			this.mq.convertAndSend(DirectConfig.MQ_NAME, "消息" + i);
		}
	}
}

@Component
class Recv {
	@RabbitListener(queues = DirectConfig.MQ_NAME)
	public void receive(String message) {
		System.out.println("消费者收到消息:" + message);
	}
}

测试类:

package com.ah.mq.direct;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DirectTest {

	@Autowired
	private DirectSendRecv mq;

	@Test
	public void send() {
		mq.send();
	}
}

Topic模式

Topic模式:

所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息。

即Topic交换机按Topic向队列发送消息,一个队列可以绑定一种Topic,也可以绑定多种。

模拟场景:

  • VIP可以收到所有消息(对应多个Topic)
  • Common用户只能收到一般消息(对应特定的Topic)

配置类,在这里创建队列、Topic交换机,并且和交换机绑定。

package com.ah.mq.topic;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.*;

@Configuration
public class TopicConfig {
	final static String EXCHANGE_NAME = "topicExchange";
	final static String QUEUE_VIP = "topic.vip";
	final static String QUEUE_COMMON = "topic.common";

	@Bean
	public Queue queueVip() {
		// 创建队列
		return new Queue(QUEUE_VIP);
	}

	@Bean
	public Queue queueCommon() {
		return new Queue(QUEUE_COMMON);
	}

	@Bean
	TopicExchange exchange() {
		// Topic交换器
		return new TopicExchange(EXCHANGE_NAME);
	}

	// 将对列绑定到Topic交换器(绑定后,修改代码不会自动解绑,要到MQ控制界面去解绑)
	// (VIP可以接收所有消息,采用#的方式)
	@Bean
	// 此处是自动绑定,参数名要求和上面的方法名一致
	Binding bindingExchangeVIP(Queue queueVip, TopicExchange exchange) {
		// with的参数是routingKey,范围比queue的那么要广
		return BindingBuilder.bind(queueVip).to(exchange).with("topic.#");
	}

	// 将对列绑定到Topic交换器
	@Bean
	Binding bindingExchange2(Queue queueCommon, TopicExchange exchange) {
		// 普通人只能接收部分消息
		return BindingBuilder.bind(queueCommon).to(exchange).with(QUEUE_COMMON);
	}
}

发送方和接收方写在一起

  • 一个发送方
  • 两个接收方(模拟VIP和普通用户)(使用@Component,自动接收,没必要使用控制器)
package com.ah.mq.topic;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TopicRendRecv {
	@Autowired
	private AmqpTemplate mq;

	public void sendVip() {
		String msg = "VIP专享";
		System.out.println("Sender:" + msg);
		this.mq.convertAndSend(TopicConfig.EXCHANGE_NAME, TopicConfig.QUEUE_VIP, msg);
	}

	public void sendAll() {
		String msg = "所有人";
		System.out.println("Sender:" + msg);
		this.mq.convertAndSend(TopicConfig.EXCHANGE_NAME, TopicConfig.QUEUE_COMMON, msg);
	}
}

@Component
class TopicReceiver {
	@RabbitListener(queues = TopicConfig.QUEUE_VIP)
	public void recv1(String message) {
		System.out.println("VIP:" + message);
	}
}

@Component
class TopicReceiver2 {
	@RabbitListener(queues = TopicConfig.QUEUE_COMMON)
	public void recv1(String message) {
		System.out.println("普通人: " + message);
	}
}

测试类

package com.ah.mq.topic;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TopicTest {

	@Autowired
	private TopicRendRecv topic;

	@Test
	public void sendVip() {
		topic.sendVip();
	}

	@Test
	public void sendAll() {
		topic.sendAll();
	}
}

运行结果:

普通人 : 所有人

VIP : 所有人

VIP : VIP专享

Fanout模式

fanout:展开、散开

广播,即所有bind到某exchange的queue,都可以接收消息。

package com.ah.mq.fanout;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.*;

@Configuration
public class FanoutConfig {
	static final String MQ_NAME_1 = "fanout.1";
	static final String MQ_NAME_2 = "fanout.2";
	static final String EXCHAGE_NAME = "fanoutExchange";

	@Bean
	public Queue queue1() {
		return new Queue(MQ_NAME_1);
	}

	@Bean
	public Queue queue2() {
		return new Queue(MQ_NAME_2);
	}

	// 创建Fanout交换器
	@Bean
	FanoutExchange fanoutExchange() {
		return new FanoutExchange(EXCHAGE_NAME);
	}

	// 绑定
	@Bean
	Binding bindingExchangeA(Queue queue1, FanoutExchange fanoutExchange) {
		return BindingBuilder.bind(queue1).to(fanoutExchange);
	}

	@Bean
	Binding bindingExchangeB(Queue queue2, FanoutExchange fanoutExchange) {
		return BindingBuilder.bind(queue2).to(fanoutExchange);
	}
}
package com.ah.mq.fanout;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FanoutSendRecv {

	@Autowired
	private AmqpTemplate mq;

	public void send() {
		String msg = "@所有人";
		System.out.println("Sender:" + msg);
		// routingKey为空字符串
		this.mq.convertAndSend(FanoutConfig.EXCHAGE_NAME, "", msg);
	}

	@RabbitListener(queues = FanoutConfig.MQ_NAME_1)
	public void process(String message) {
		System.out.println("Recv1:" + message);
	}
}

@Component
class ClientA {
	@RabbitListener(queues = FanoutConfig.MQ_NAME_2)
	public void process(String message) {
		System.out.println("Recv2:" + message);
	}
}
package com.ah.mq.fanout;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class FanoutTest {

	@Autowired
    private FanoutSendRecv send;

    @Test
    public void sendFanout () {
    	send.send();
    }
}

Header类型

使用较少,用Headers来匹配。

Headers是一个键值对,可以定义成Hashtable。

发送者在发送的时候定义一些键值对,接收者也可以再绑定时候传入一些键值对,两者匹配的话,则对应的队列就可以收到消息。

匹配有两种方式all和any。

  • all代表定义的多个键值对都要满足,

  • any只要满足一个即可。

package com.ah.mq.headers;

import java.util.HashMap;
import java.util.Map;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.*;

@Configuration
public class HeadersConfig {
	static final String MQ_NAME_ALL = "HeaderQueue1";
	static final String MQ_NAME_ANY = "HeaderQueue2";
	static final String EXCHAGE_NAME_ALL = "HeaderExchange1";
	static final String EXCHAGE_NAME_ANY = "HeaderExchange2";

	@Bean
	public Queue queue1() {
		return new Queue(MQ_NAME_ALL);
	}

	@Bean
	public Queue queue2() {
		return new Queue(MQ_NAME_ANY);
	}

	@Bean
	public HeadersExchange exchange1() {
		return new HeadersExchange(EXCHAGE_NAME_ALL);
	}

	@Bean
	public HeadersExchange exchange2() {
		return new HeadersExchange(EXCHAGE_NAME_ANY);
	}

	static private Map<String, Object> map = new HashMap();
	static {
		map.put("key1", "v1");
		map.put("key2", "v2");
	}

	@Bean
	public Binding bindingExchange1(Queue queue1, HeadersExchange exchange1) {
		return BindingBuilder.bind(queue1).to(exchange1).whereAll(map).match();
	}

	@Bean
	public Binding bindingExchange2(Queue queue2, HeadersExchange exchange2) {
		return BindingBuilder.bind(queue2).to(exchange2).whereAny(map).match();
	}
}
package com.ah.mq.headers;

import java.util.Map;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.amqp.support.converter.SimpleMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HeadersSendRecv {

	@Autowired
	private AmqpTemplate mq;

	public void sendALL(Map<String, Object> head, String msg) {
		mq.convertAndSend(HeadersConfig.EXCHAGE_NAME_ALL, HeadersConfig.MQ_NAME_ALL, getMessage(head, msg));
	}

	public void sendAny(Map<String, Object> head, String msg) {
		mq.convertAndSend(HeadersConfig.EXCHAGE_NAME_ANY, HeadersConfig.MQ_NAME_ANY, getMessage(head, msg));
	}

	private Message getMessage(Map<String, Object> head, Object msg) {
		MessageProperties messageProperties = new MessageProperties();
		for (Map.Entry<String, Object> entry : head.entrySet()) {
			messageProperties.setHeader(entry.getKey(), entry.getValue());
		}
		MessageConverter messageConverter = new SimpleMessageConverter();
		return messageConverter.toMessage(msg, messageProperties);
	}
}

@Component
class HeaderRecv {

	@RabbitListener(queues = HeadersConfig.MQ_NAME_ALL)
	public void creditBank(String msg) {
		System.out.println("RECV:要求ALL," + msg);
	}

	@RabbitListener(queues = HeadersConfig.MQ_NAME_ANY)
	public void creditFinance(String msg) {
		System.out.println("RECV:要求ANY," + msg);
	}
}
package com.ah.mq.headers;

import java.util.HashMap;
import java.util.Map;

import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class HeadersTest {

	@Autowired
	private HeadersSendRecv sender;
	
	static Map<String, Object> head = new HashMap();
	@Before
	public void init() {
		head.clear();
	}

	@Test
	public void t1() {
		head.put("key1", "v1");
		sender.sendALL(head, "实际Part");
	}

	@Test
	public void t2() {
		head.put("key1", "v1");
		head.put("key2", "v2");
		sender.sendALL(head, "实际All");
	}

	@Test
	public void t3() {
		head.put("key2", "v2");
		sender.sendAny(head, "实际Part");
	}

	@Test
	public void t4() {
		head.put("key1", "v1");
		head.put("key2", "v2");
		sender.sendAny(head, "实际All");
	}
}

结果:

RECV:要求ANY,实际Part

RECV:要求ALL,实际All

RECV:要求ANY,实际All

(要求ALL,实际Part的,没有收到)

删除队列

关闭应用:rabbitmqctl stop_app
清除队列:rabbitmqctl reset
重新启动:rabbitmqctl start_app
posted @ 2020-05-24 17:46  虎老狮  阅读(292)  评论(0编辑  收藏  举报