RabbitMQ
同步通讯的优缺点
优点:
·时效性较强,可以立即得到结果
缺点:
·耦合度高
·性能和吞吐能力下降
·有额外的资源消耗
·有级联失败问题
异步通讯
优点:
·不需要等待服务的结果
·性能提升,吞吐量提升
·服务间耦合度减少
·没有级联失败
·流量削峰(高并发场景下常用)
缺点:
·依赖于Broker的可靠性、安全性、吞吐能力架构复杂了
·业务没有明显的流程线,不好追踪管理
常见的 MQ (消息队列:事件队列)

消息队列的收发流程 (后面的框架封装了这些流程)
1,发消息:与消息中间件MQ服务器建立连接、创建通道(channel)、通过通道声明队列(queue)、利用通道向队列发送消息
2,收消息:建立连接、创建通道、声明队列、定义消费行为 handleDelivery(),利用管道将消费者与队列绑定
消费者:指的是消息接收方
SpringAMQP-(Advanced Message Queuing Protocol)
该框架提供:
1:自动声明队列,交换机,及绑定关系
2:基于注解的监听器模式,异步接收消息
3:封装了 RabbitTemplate 工具,用于发送消息
配置方式:
spring:
rabbitmq:
host: 127.0.0.1 # MQ服务器主机IP
port: 5672 # 端口
virtual-host: / # 虚拟主机
username: guest # 用户名
password: guest # 密码
当消息很多的时候,为了提高消息处理的速度,
可以通过控制消费者的预取的消息数量(prefecth),
让处理速度快的消费者可以处理更多的消息,而不会导致消息堆积
加入配置
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息
简单队列 Basic Queue
最普通的消息收发队列,可用性不太高,不够灵活,不推荐
示例(关键代码)
// 发送方
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSimpleQueue() {
// 队列名称
String queueName = "simple.queue";
// 消息
String message = "hello, spring amqp!";
// 发送消息
rabbitTemplate.convertAndSend(queueName, message);
}
-------------------------------------------------
// 接收方
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "simple.queue")
public void listenSimpleQueueMessage(String msg) throws InterruptedException {
System.out.println("spring 消费者接收到消息:【" + msg + "】");
}
}
任务模型 Work Queue
多个消费者绑定到一个队列,同一条消息只会被一个消费者处理
示例
// 发送方
/**
* workQueue
* 向队列中不停发送消息,模拟消息堆积。
*/
@Test
public void testWorkQueue() throws InterruptedException {
// 队列名称
String queueName = "simple.queue";
// 消息
String message = "hello, message_";
for (int i = 0; i < 50; i++) {
// 发送消息
rabbitTemplate.convertAndSend(queueName, message + i);
Thread.sleep(20);
}
}
-----------------------------------------
// 接收方
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(20);
}
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(200);
}
发布-订阅
加入了交换机 Exchange

- Publisher:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
- Exchange:交换机,一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有以下3种类型:
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key 的队列
- Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
- Consumer:消费者,与以前一样,订阅队列,没有变化
- Queue:消息队列也与以前一样,接收消息、缓存消息。
Fanout 扇出(类似广播)
特点:该模式下的消息收取都会由交换机管理,并通过广播的形式,传递给该交换机绑定的所有队列,即所有的消费者都能收到同样的消息
缺点:配置 bean 太过于繁琐。不推荐使用
Direct 路由模式
特点:该模式下的消息经交换机,只路由给指定的key对应的队列,其他的队列不可接收
示例(关键代码)
基于@Bean的方式声明队列和交换机比较麻烦,Spring还提供了基于注解方式来声明。
在consumer的SpringRabbitListener中添加两个消费者,同时基于注解来声明队列和交换机:
// 接收方
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red", "blue"}
))
public void listenDirectQueue1(String msg){
System.out.println("消费者接收到direct.queue1的消息:【" + msg + "】");
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red", "yellow"}
))
public void listenDirectQueue2(String msg){
System.out.println("消费者接收到direct.queue2的消息:【" + msg + "】");
}
--------------------------------------
// 发送方
@Test
public void testSendDirectExchange() {
// 交换机名称
String exchangeName = "itcast.direct";
// 消息
String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "red", message);
}
Topic
特点:该模式与 direct 相似,但是加入了通配符#、*、且路由routekey的名称都是一个或多个单词通过.连接组成
通配符规则:
#:匹配一个或多个词
*:匹配不多不少恰好1个词
举例:
item.#:能够匹配 item.spu.insert 或者 item.spu
item.*:只能匹配 item.spu