1消息队列基础
1.1同步异步
同步调用
-
时效性强
-
拓展性差
-
性能下降
-
级联失败
异步调用
-
业务解耦,拓展性强
-
无需等待,性能好
-
故障隔离
-
缓存信息,流量削峰填谷
-
时效性差
-
不确定执行是否成功
1.2MQ技术选型
2安装
2.1安装步骤
linux环境,用docker安装
-
下载rabbitmq镜像
dockerdocker pull rabbitmq:3.7.15
-
创建容器
dockerdocker run -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123456 -v mq-plugins:/plugins --name rabbitmq --hostname myrabbitmq -p 15672:15672 -p 5672:5672 -d rabbitmq:3.7.15
-
查看容器
dockerdocker ps
-
配置管理页面插件
dockerdocker exec -it rabbitmq /bin/bash rabbitmq-plugins enable rabbitmq_management
-
访问管理页面ip:15672,账号密码为创建容器的RABBITMQ_DEFAULT信息
2.2架构和概念
-
publisher:消息发送者
-
consumer:消息消费者
-
queue:队列,负责存储消息
-
exchange:交换机,负责路由转发消息
-
virtual-host:虚拟主机,负责数据隔离
3Java客户端
3.1AMQP
高级消息队列协议
Spring AMQP:基于AMQP协议定义的一套API规范
3.2简单发送接收消息
rabbitmq管理页面创建队列testQueue
微服务项目,引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置rabbitmq信息
yml
spring:
rabbitmq:
host: ip
port: 5672
virtual-host: /
username: username
password: password
发送,用单元测试
java
@SpringBootTest(classes = UserApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void test() {
String queue = "testQueue";
String message = "hello";
rabbitTemplate.convertAndSend(queue, message);
}
}
接收
java
@Component
public class MqListener {
@RabbitListener(queues = "testQueue")
public void listen(String message) {
System.out.println("msg=" + message);
}
}
3.3work模型
rabbitmq管理页面创建队列workQueue
多个消费者绑定到一个队列,加快消息处理速度
同一条消息只会被一个消费者处理
设置prefetch控制消费者预取消息数量
消费者配置
yml
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一条
发送
java
@Test
void testWorkQueue() {
String queue = "workQueue";
for (int i = 1; i <= 50; i++) {
String message = "hello" + i;
rabbitTemplate.convertAndSend(queue, message);
}
}
接收
java
@RabbitListener(queues = "workQueue")
public void listenWorkQueue1(String message) throws InterruptedException {
System.out.println("listen1,msg=" + message);
Thread.sleep(20);
}
@RabbitListener(queues = "workQueue")
public void listenWorkQueue2(String message) throws InterruptedException {
System.out.println("listen2,msg=" + message);
Thread.sleep(200);
}
listen1处理速度更快,因此处理消息数量更多
3.4交换机
3.4.1Fanout
广播模式,分发每个队列
rabbitmq管理页面创建队列q1,q2,绑定amq.fanout交换机
发送
java
@Test
void testFanout() {
String exchange = "amq.fanout";
String message = "hello fanout";
rabbitTemplate.convertAndSend(exchange, "", message);
}
接收
java
@RabbitListener(queues = "q1")
public void listenFanout1(String message) {
System.out.println("listenfanout1,msg=" + message);
}
@RabbitListener(queues = "q2")
public void listenFanout2(String message) {
System.out.println("listenfanout2,msg=" + message);
}
3.4.2Direct
定向路由,每个队列绑定交换机时设置一个routingKey,发送消息指定routingKey发送到指定队列
rabbitmq管理页面创建队列directQueue1,directQueue2,分别绑定amq.direct交换机,设置routingKey为direct1,direct2
发送
java
@Test
void testDirect() {
String exchange = "amq.direct";
String message = "hello direct";
String routingKey = "direct1";
rabbitTemplate.convertAndSend(exchange, routingKey, message);
}
接收
java
@RabbitListener(queues = "directQueue1")
public void listenDirect1(String message) {
System.out.println("listenDirect1,msg=" + message);
}
@RabbitListener(queues = "directQueue2")
public void listenDirect2(String message) {
System.out.println("listenDirect2,msg=" + message);
}
3.4.3Topic
与Direct类似,routingKey可以是多个单词的列表,以.分割,routingKey可以指定通配符
-
#:0个或多个单词
-
*:一个单词
rabbitmq管理页面创建队列topicQueue1,topicQueue2,分别绑定amq.topic交换机,设置routingKey为topic1.#,topic2.#
发送
java
@Test
void testTopic() {
String exchange = "amq.topic";
String message = "hello topic";
String routingKey = "topic1.aaa";
rabbitTemplate.convertAndSend(exchange, routingKey, message);
}
接收
java
@RabbitListener(queues = "topicQueue1")
public void listenTopic1(String message) {
System.out.println("listenTopic1,msg=" + message);
}
@RabbitListener(queues = "topicQueue2")
public void listenTopic2(String message) {
System.out.println("listenTopic2,msg=" + message);
}
3.5声明队列和交换机
3.5.1Bean配置
java
@Configuration
public class FanoutConfig {
@Bean
public FanoutExchange fanoutExchange() {
// 二选一
// return ExchangeBuilder.fanoutExchange("myFanout");
return new FanoutExchange("myFanout");
}
@Bean
public Queue queue() {
// 二选一
// return QueueBuilder.durable("myQueue");
return new Queue("myQueue");
}
@Bean
public Binding binding(Queue queue, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queue).to(fanoutExchange);
}
}
3.5.2注解配置
java
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "myQueue", durable = "true"),
exchange = @Exchange(name = "myDirect", type = ExchangeTypes.DIRECT),
key = {"red"}
))
public void listenDirect(String message) {
System.out.println("listenDirect,msg=" + message);
}
3.6消息转换器
创建队列objectQueue
发送java对象或者map
引入依赖
xml
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
发送方、接收方添加消息转换器
java
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
发送
java
@Test
void testSendObject() {
String queue = "objectQueue";
Map<String, Object> map = new HashMap<>();
map.put("id", "1");
map.put("name", "hhh");
rabbitTemplate.convertAndSend(queue, map);
}
接收
java
@RabbitListener(queues = "objectQueue")
public void listenObject(Map<String, Object> map) {
System.out.println("listenObject,msg=" + map);
}
其它消息队列知识看JavaGuide 消息队列