同步和异步通讯

微服务间通讯有同步和异步两种方式:

同步通讯:就像打电话,需要实时响应。

异步通讯:就像发邮件,不需要马上回复。

同步通讯

优点:时效性较强,可以立即得到结果

缺点:

  • 耦合度高
  • 性能和吞吐能力下降
  • 有额外的资源消耗
  • 有级联失败问题

异步通讯

优点:

  • 吞吐量提升:无需等待订阅者处理完成,响应更快速
  • 故障隔离:服务没有直接调用,不存在级联失败问题
  • 调用间没有阻塞,不会造成无效的资源占用
  • 耦合度极低,每个服务都可以灵活插拔,可替换
  • 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件

缺点:

  • 架构复杂了,业务没有明显的流程线,不好管理
  • 需要依赖于Broker的可靠、安全、性能

MQ

MQ中文是消息队列(MessageQueue),字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。

常见的MQ对比

RABBITMQ

ACTIVEMQ

ROCKETMQ

KAFKA

公司/社区

Rabbit

Apache

阿里

Apache

开发语言

Erlang

Java

Java

Scala&Java

协议支持

AMQP,XMPP,SMTP,STOMP

OpenWire,STOMP,REST,XMPP,AMQP

自定义协议

自定义协议

可用性


一般



单机吞吐量

一般



非常高

消息延迟

微秒级

毫秒级

毫秒级

毫秒以内

消息可靠性


一般


一般

MQ的基本结构:

RabbitMQ中的一些角色:

  • publisher:生产者
  • consumer:消费者
  • exchange个:交换机,负责消息路由
  • queue:队列,存储消息
  • virtualHost:虚拟主机,隔离不同租户的exchange、queue、消息的隔离

RabbitMQ消息模型

主要有三种类型:

基本消息队列:一个生产者传递给一个消费者处理。

工作消息队列:一个生产者传递给多个消费者处理

发布订阅:

而发布订阅中又分为

fanout广播:一个生产者传递给多个消费者处理,每个消费者都会收到同样的信息。

direct路有:根据routingkey来判断,每个队列都有自己的key,路由中的key会传到对应的key的队列。

topic主题: 根据routingkey来判断,#表示一个或多个,*表示一个。

基础操作

在父工程中导入依赖

<!--AMQP依赖,包含RabbitMQ-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>

 配置MQ地址,在publisher服务的application.yml中添加配置:

spring:
   rabbitmq:
     host: 192.168.94.130 # 主机名
     port: 5672 # 端口
     virtual-host: / # 虚拟主机
     username: itcast # 用户名
     password: itcast142 # 密码

编写测试类

@SpringBootTest
public class SpringAmqpTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSimpleQueue() {
        // 队列名称
        String queueName = "simple.queue";
        // 消息
        String message = "hello, spring amqp!";
        // 发送消息
        rabbitTemplate.convertAndSend(queueName, message);
    }
}

消息接收也要配置MQ地址

spring:
   rabbitmq:
     host: 192.168.94.130 # 主机名
     port: 5672 # 端口
     virtual-host: / # 虚拟主机
     username: itcast # 用户名
     password: itcast142 # 密码

然后在consumer服务的cn.itcast.mq.listener包中新建一个类SpringRabbitListener,代码如下:

package cn.itcast.mq.listener;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class SpringRabbitListener {

    @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueueMessage(String msg) throws InterruptedException {
        System.out.println("spring 消费者接收到消息:【" + msg + "】");
    }
}

 注释形式

由于配置类添加bean比较麻烦,我们也可以选择用配置类来进行操作。

@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 + "】");
}

总结

总结

描述下Direct交换机与Fanout交换机的差异?

  • Fanout交换机将消息路由给每一个与之绑定的队列
  • Direct交换机根据RoutingKey判断路由给哪个队列
  • 如果多个队列具有相同的RoutingKey,则与Fanout功能类似

基于@RabbitListener注解声明队列和交换机有哪些常见注解?

  • @Queue
  • @Exchange

消息转换器

将对象转成json格式保存,节省空间。

导入依赖

<dependency>
     <groupId>com.fasterxml.jackson.dataformat</groupId>
     <artifactId>jackson-dataformat-xml</artifactId>
     <version>2.9.10</version>
 </dependency>

 在启动类中添加一个Bean即可:

@Bean
 public MessageConverter jsonMessageConverter(){
     return new Jackson2JsonMessageConverter();
 }