同步调用优缺点
优点
- 时效性较强,可以立即得到结果
缺点
- 耦合度高
- 性能和吞吐能力下降
- 有额外的资源消耗
- 有级联失败问题
异步通信优缺点
优点
- 耦合度低
- 吞吐量提升
- 故障隔离
- 流量削峰
缺点
- 依赖于Broker的可靠性、安全性、吞吐能力
- 架构复杂了,业务没有明显的流程线,不好追踪管理
mq常见技术
RabbitMQ概述
概念
- channel:操作MQ的工具
- exchange:路由消息到队列中
- queue:缓存消息
- virtual host:虚拟主机,是对queue、exchange等资源的逻辑分组
常见消息模型
MQ的官方文档中给出了5个MQ的Demo示例
- 基本消息队列(basicqueue)
- 工作消息队列(workqueue)
- 多个消费者绑定到一个队列,同一条消息只会被一个消费者处理
- 通过设置prefetch来控制消费者预取的消息数量。防止效率低的消费者取到太多消息,降低效率
- 发布订阅(pub,sub)
- 广播(fanout)
- 发送给所有队列
- 发送给所有队列
- 路由(direct/routing)
- 发送到指定队列
- 发送到指定队列
- 主题(topic)
- 分类配置
- 分类配置
- 广播(fanout)
SpringAMQP
AMOP
一种mq标准
SpringAMQP
基于AMQP协议定义的API规范,提供模板发送和接收消息
- 包含两部分
- spring-amqp:基础抽象
- spring-rabbit:底层实现
Rabbit基本消息队列示例
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
virtual-host: /
username: zxy
password: 123456
//生产者
@SpringBootTest
public class test {
@Autowired
private RabbitTemplate rabbitTemplate;
// 不会自动创建队列
@Test
public void testSimpleQueue(){
String queueName = "simple.queue";
String message = "hello,spring amqp";
rabbitTemplate.convertAndSend(queueName,message);
}
}
//消费者
@Component
public class ConsumerMessage {
@RabbitListener(queues = "simple.queue")
public void listener(String msg){
System.out.println("我来消费了:"+msg);
}
}
Rabbit工作消息队列示例
#消费者配置
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
virtual-host: /
username: zxy
password: 123456
listener:
simple:
prefetch: 1 #预取消息数
//消费者代码
@RabbitListener(queues = "simple.queue")
public void listenerWork1(String msg) throws InterruptedException {
System.out.println("我来消费了1:"+msg+":"+ LocalTime.now());
// Thread.sleep(20);
}
@RabbitListener(queues = "simple.queue")
public void listenerWork2(String msg) throws InterruptedException {
System.out.println("我来消费了2:"+msg+":"+ LocalTime.now());
// Thread.sleep(20);
}
RabbitMQ发布订阅(广播)
//交换机、队列绑定关系配置
@Configuration
public class FonoutConfig {
//声明交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("itcast.fanout");
}
//声明第一个队列
@Bean
public Queue fanoutQueue1(){
return new Queue("fanout.queue1");
}
//声明第二个队列
@Bean
public Queue fanoutQueue2(){
return new Queue("fanout.queue2");
}
//绑定队列1和交换机
@Bean
public Binding bindingQueue1(){
return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
}
//绑定队列2和交换机
@Bean
public Binding bindingQueue2(){
return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
}
}
//消息生产者
@SpringBootTest
public class test {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void sendFanout(){
String exchange = "itcast.fanout";
String message = "你好,...";
rabbitTemplate.convertAndSend(exchange,"",message);
}
}
//消息消费者
@RabbitListener(queues = "fanout.queue1")
public void listenerFanout1(String msg){
System.out.println("我来消费了1:"+msg);
}
@RabbitListener(queues = "fanout.queue2")
public void listenerFanout2(String msg){
System.out.println("我来消费了2:"+msg);
}
RabbitMQ发布订阅(路由)
- 每一个Queue都与Exchange设置一个BindingKey
- 发布者发送消息时指定消息的RoutingKey
- Exchange将消息路由到BindingKey与消息RoutingKey一致的队列
//消费者代码
@Component
public class ConsumerMessage {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
key = {"red","blue"}
))
public void listenerRout1(String msg) {
System.out.println("我来消费了1:"+msg+":"+ LocalTime.now());
// Thread.sleep(20);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"),
exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
key = {"yellow","green"}
))
public void listenerRout2(String msg) {
System.out.println("我来消费了2:"+msg+":"+ LocalTime.now());
// Thread.sleep(20);
}
}
//生产者
@SpringBootTest
public class test {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void sendFanout(){
String exchange = "itcast.direct";
String message = "你好,...";
rabbitTemplate.convertAndSend(exchange,"green",message);
}
}
RabbitMQ发布订阅(主题)
//消费者代码
@Component
public class ConsumerMessage {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue1"),
exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
key = "china.#"
))
public void listenerRout1(String msg) {
System.out.println("我来消费了1:"+msg+":"+ LocalTime.now());
// Thread.sleep(20);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue2"),
exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
key = "#.news"
))
public void listenerRout2(String msg) {
System.out.println("我来消费了2:"+msg+":"+ LocalTime.now());
// Thread.sleep(20);
}
}
//生产者
@SpringBootTest
public class test {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void sendFanout(){
String exchange = "itcast.topic";
String message = "你好,...";
rabbitTemplate.convertAndSend(exchange,"aa.news",message);
}
}
消息转换器
- 利用MessageConverter实现,默认是JDK的序列化
- 生产者和消费者必须使用相同的MessageConverter
<!-- 生产者和消费者导入相同的依赖 -->
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
</dependencies>
// 生产者消费者创建相同的bean
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
// 消费者接收参数类型与生产者保持一致
@RabbitListener(queues = "object.queue")
public void listenerRout2(Map<String,Object> map) {
System.out.println("我来消费了2:"+map+":"+ LocalTime.now());
}