首页 > 其他分享 >review-消息中间件MQ

review-消息中间件MQ

时间:2024-11-21 18:42:43浏览次数:3  
标签:String 队列 review 发送 交换机 MQ 消息 消息中间件 msg

RabbitMQ

RabbitMQ,作为当今流行的开源消息代理软件,以其卓越的可靠性、灵活性和易用性在微服务架构和分布式系统中扮演着至关重要的角色。它不仅能够确保消息在不同系统组件间的高效传递,还能通过其高级消息队列协议(AMQP)支持复杂的路由功能,从而满足各种消息分发场景。RabbitMQ的高性能和可扩展性使其成为处理大规模消息传递任务的理想选择,同时,其丰富的API和工具集也极大地简化了开发人员在不同编程环境中的集成和使用。无论是应对日常的消息传递需求,还是构建复杂的事件驱动架构,RabbitMQ都能提供强大而稳定的支持。

kafka消息中间件在上一篇文章SpringBoot3全面复习已经写过,下面主要介绍RabbitMQ的内容。
一、初始MQ
同步调用的优势是,时效性强,等待到结果后返回。
在这里插入图片描述
异步调用
在这里插入图片描述
在异步调用中,发送者不再直接同步调用接收者的业务接口,而是发送一条消息投递给消息Broker。然后接收者根据自己的需求从消息Broker那里订阅消息。每当发送方发送消息后,接受者都能获取消息并处理。
综上,异步调用的优势包括:

  • 耦合度更低 ,扩展性强
  • 异步调用,无需等待,性能更好
  • 缓存消息,流量削峰填谷
  • 故障隔离,避免级联失败

当然,异步通信也并非完美无缺,它存在下列缺点:

  • 完全依赖于Broker的可靠性、安全性和性能 架构复杂,
  • 后期维护和调试麻烦
    技术选型
    消息Broker,目前常见的实现方案就是消息队列(MessageQueue),简称为MQ.
    在这里插入图片描述
    追求可用性:Kafka、 RocketMQ 、RabbitMQ
    追求可靠性:RabbitMQ、RocketMQ
    追求吞吐能力:RocketMQ、Kafka
    追求消息低延迟:RabbitMQ、Kafka
    安装RabbitMQ
    同样基于Docker来安装RabbitMQ,使用下面的命令即可:
docker run \
 -e RABBITMQ_DEFAULT_USER=itheima \
 -e RABBITMQ_DEFAULT_PASS=123321 \
 -v mq-plugins:/plugins \
 --name mq \
 --hostname mq \
 -p 15672:15672 \
 -p 5672:5672 \
 --network hmall \
 -d \
 rabbitmq:3.8-management

同样基于Docker来安装RabbitMQ,使用下面的命令即可:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
15672:RabbitMQ提供的管理控制台的端口
5672:RabbitMQ的消息发送处理接口
安装完成后,我们访问 http://虚拟机ip:15672即可看到管理控制台。首次访问需要登录,默认的用户名和密码在配置文件中已经指定了。
在这里插入图片描述
在这里插入图片描述
publisher:生产者,也就是发送消息的一方
consumer:消费者,也就是消费消息的一方
queue:队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理
exchange:交换机,负责消息路由转发,没有存储消息的能力。生产者发送的消息由交换机决定投递到哪个队列。
virtual host:虚拟主机,起到数据隔离的作用。每个虚拟主机相互独立,有各自的exchange、queue
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
数据隔离
在这里插入图片描述
Name:itheima,也就是用户名
Tags:administrator,说明itheima用户是超级管理员,拥有所有权限
Can access virtual host: /,可以访问的virtual host,这里的/是默认的virtual host
此时hmall用户没有任何virtual host的访问权限在这里插入图片描述
在这里插入图片描述
SpringAMQP
在这里插入图片描述
在这里插入图片描述

已有依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>cn.itcast.demo</groupId>
    <artifactId>mq-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>publisher</module>
        <module>consumer</module>
    </modules>
    <packaging>pom</packaging>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/>
    </parent>
 
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--AMQP依赖,包含RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
</project>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

消息发送
首先配置MQ地址,在publisher服务的application.yml中添加配置:

spring:
  rabbitmq:
    host: 192.168.150.101 # 你的虚拟机IP
    port: 5672 # 端口
    virtual-host: /hmall # 虚拟主机
    username: hmall # 用户名
    password: 123 # 密码

编写测试类,利用rabbitTemplate实现消息发送:

package com.itheima.publisher.amqp;
 
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
 
@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地址,在consumer服务的application.yml中添加配置:

spring:
  rabbitmq:
    host: 192.168.150.101 # 你的虚拟机IP
    port: 5672 # 端口
    virtual-host: /hmall # 虚拟主机
    username: hmall # 用户名
    password: 123 # 密码

consumer服务的com.itheima.consumer.listener包中新建一个类SpringRabbitListener,代码如下:

package com.itheima.consumer.listener;
 
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
 
@Component
public class SpringRabbitListener {
	// 利用RabbitListener来声明要监听的队列信息
    // 将来一旦监听的队列中有了消息,就会推送给当前服务,调用当前方法,处理消息。
    // 可以看到方法体中接收的就是消息体的内容
    @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueueMessage(String msg) throws InterruptedException {
        System.out.println("spring 消费者接收到消息:【" + msg + "】");
    }
}

WorkQueues模型
在这里插入图片描述
在这里插入图片描述当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。
此时就可以使用work 模型,多个消费者共同处理消息处理,消息处理的速度就能大大提高了。
在这里插入图片描述
消息发送

这次我们循环发送,模拟大量消息堆积现象。
在publisher服务中的SpringAmqpTest类中添加一个测试方法:

/**
     * workQueue
     * 向队列中不停发送消息,模拟消息堆积。
     */
@Test
public void testWorkQueue() throws InterruptedException {
    // 队列名称
    String queueName = "simple.queue";
    // 消息
    String message = "hello, message_";
    for (int i = 0; i < 50; i++) {
        // 发送消息,每20毫秒发送一次,相当于每秒发送50条消息
        rabbitTemplate.convertAndSend(queueName, message + i);
        Thread.sleep(20);
    }
}

消息接受
要模拟多个消费者绑定同一个队列,我们在consumer服务的SpringRabbitListener中添加2个新的方法:

@RabbitListener(queues = "work.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
    System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(20);
}
 
@RabbitListener(queues = "work.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
    System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(200);
}

消费者1 sleep了20毫秒,相当于每秒钟处理50个消息
消费者2 sleep了200毫秒,相当于每秒处理5个消息
在这里插入图片描述
也就是说消息是平均分配给每个消费者,并没有考虑到消费者的处理能力。导致1个消费者空闲,另一个消费者忙的不可开交。没有充分利用每一个消费者的能力,最终消息处理的耗时远远超过了1秒。这样显然是有问题的。
能者多劳
修改consumer服务的application.yml文件,添加配置:

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

由于消费者1处理速度较快,所以处理了更多的消息;消费者2处理速度较慢,只处理了6条消息。而最终总的执行耗时也在1秒左右,大大提升。
正所谓能者多劳,这样充分利用了每一个消费者的处理能力,可以有效避免消息积压问题。
在这里插入图片描述
交换机
在这里插入图片描述
Fanout:广播,将消息交给所有绑定到交换机的队列。我们最早在控制台使用的正是Fanout交换机
Direct:订阅,基于RoutingKey(路由key)发送给订阅了消息的队列
Topic:通配符订阅,与Direct类似,只不过RoutingKey可以使用通配符
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失
在这里插入图片描述
1) 可以有多个队列
2) 每个队列都要绑定到Exchange(交换机)
3) 生产者发送的消息,只能发送到交换机
4) 交换机把消息发送给绑定过的所有队列
5) 订阅队列的消费者都能拿到消息

在这里插入图片描述
消息发送

@Test
public void testFanoutExchange() {
    // 交换机名称
    String exchangeName = "hmall.fanout";
    // 消息
    String message = "hello, everyone!";
    rabbitTemplate.convertAndSend(exchangeName, "", message);
}

消息接收

@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String msg) {
    System.out.println("消费者1接收到Fanout消息:【" + msg + "】");
}
 
@RabbitListener(queues = "fanout.queue2")
public void listenFanoutQueue2(String msg) {
    System.out.println("消费者2接收到Fanout消息:【" + msg + "】");
}

在这里插入图片描述
一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。
Direct交换机
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
消息接收
在consumer服务的SpringRabbitListener中添加方法:

@RabbitListener(queues = "direct.queue1")
public void listenDirectQueue1(String msg) {
    System.out.println("消费者1接收到direct.queue1的消息:【" + msg + "】");
}
 
@RabbitListener(queues = "direct.queue2")
public void listenDirectQueue2(String msg) {
    System.out.println("消费者2接收到direct.queue2的消息:【" + msg + "】");
}

消息发送

@Test
public void testSendDirectExchange() {
    // 交换机名称
    String exchangeName = "hmall.direct";
    // 消息
    String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
    // 发送消息
    rabbitTemplate.convertAndSend(exchangeName, "red", message);
}

在这里插入图片描述
Topic
在这里插入图片描述
Queue1:绑定的是china.# ,因此凡是以 china.开头的routing key 都会被匹配到。包括china.news和china.weather
Queue2:绑定的是#.news ,因此凡是以 .news结尾的 routing key 都会被匹配。包括china.news和japan.news
在这里插入图片描述
.消息发送

/**
     * topicExchange
     */
@Test
public void testSendTopicExchange() {
    // 交换机名称
    String exchangeName = "itcast.topic";
    // 消息
    String message = "喜报!孙悟空大战哥斯拉,胜!";
    // 发送消息
    rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
}

.消息接收

@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "topic.queue1"),
    exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
    key = "china.#"
))
public void listenTopicQueue1(String msg){
    System.out.println("消费者接收到topic.queue1的消息:【" + msg + "】");
}

@RabbitListener(bindings = @QueueBinding(
    value = @Queue(name = "topic.queue2"),
    exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
    key = "#.news"
))
public void listenTopicQueue2(String msg){
    System.out.println("消费者接收到topic.queue2的消息:【" + msg + "】");
}

在这里插入图片描述

声明队列交换机
在这里插入图片描述

Exchange 只是一个接口,其具体的实现类分别是对应的几个不同类型的交换机,如 FanoutExchange 、DirectExchange 、TopicExchange 等等。

在这里插入图片描述
其中绑定关系的构建是 BindingBuilder.bind(队列).to(交换机).with(RoutingKey) 这样的。

@RabbitListener 注解声明
可以使用当时用于定义消费者的注解 @RabbitListener 来定义队列、交换机、及绑定关系,只需其中的 bindings 属性,在其中使用 @QueueBinding 注解进行定义。
在这里插入图片描述
1.value = @Queue(…) 定义了队列的具体属性。
2.exchange = @Exchange(…) 指定关联的交换机详情。
3.key = {“hi”} 设置了绑定的路由键。
.消息转换器
在 Spring AMQP 在内部进行消息转化的时候会使用 JDK 自带的序列化方式,这种方法存在着问题,首先 JDK 的序列化存在安全风险,反序列化时容易被代码注入,其次,序列化后的消息占用空间太多,可读性差。

在这里插入图片描述
在这里插入图片描述
建议使用 JSON 序列化代替默认的 JDK 序列化。
在这里插入图片描述

1.在消息的接收者和消费者中都引入 jackson 的依赖

        <!--jackson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.4</version>
        </dependency>

两者中都要配置 MessageConverter 成 Bean(可在启动类中配置)
在这里插入图片描述
发送者的可靠性
发送者重连
在这里插入图片描述
发送者确认机制
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
MQ的可靠性
在这里插入图片描述
数据持久化
在这里插入图片描述
在这里插入图片描述
在默认情况下是非持久的,可以选择 2 发送持久化的消息,而 Spring AMQP 发送的消息默认是持久化的,我们也可以通过自定义构建消息来发送非持久化的消息。
在这里插入图片描述

Message message = MessageBuilder
        .withBody("holle, SpringAMQP".getBytes(StandardCharsets.UTF_8))
        .setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT)
        .build();

其中 setDeliveryMode 用于设置投递模式为持久化或非持久化。 持久化的优点在于重启后,持久化的交换机、队列、消息仍然会存在,提高了效率。
Lazy Queue(惰性队列)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接收者的可靠性
消费者确认机制
在这里插入图片描述

spring:
  rabbitmq:
    listener:
      simple:
        ackonwledge-mode: auto # 配置为 auto 模式

失败重试机制
Spring AMQP 提供了消费者重试机制,在消费者出现异常时利用本地重试,而不是无限的发送消息到 MQ 中,我们可以通过在 yml 配置文件中添加相关配置来开启重试机制。

spring:
  rabbitmq:
    listener:
      simple:
        retry:
          enabled: true      # 开启重试机制
          initial-interval: 1000ms # 第一次重试间隔时间
          multiplier: 1      # 失败后重试间隔倍数
          max-attempts: 3    # 最大重试次数
          stateless: true    # true无状态;false有状态。如果业务中包含事务,则设置为false

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
业务幂等性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
延迟消息
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
延迟消息插件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

标签:String,队列,review,发送,交换机,MQ,消息,消息中间件,msg
From: https://blog.csdn.net/w287586/article/details/143834096

相关文章

  • RocketMQ多个消费组消费同一个topic,其中有一个组正常消费,其余均异常堆积
    @Service@RocketMQMessageListener(consumerGroup="${rocketmq.consumer.group}",topic="${rocketmq.consumer.topic}")publicclassMsgListenerimplementsRocketMQListener<MessageExt>,RocketMQPushConsumerLifecycleListener{priv......
  • Code Review 指导方针
    优质博文:IT-BLOG-CNWhyCodeReview?-为什么要进行代码评审?CodeReview是软件开发过程中的一个关键实践,它有以下几个重要目的:ImproveCodeQuality-改进代码质量【1】确保代码符合团队的编码标准、最佳实践和设计原则。【2】识别并修正可能影响长期维护的问题,如......
  • RabbitMQ
    RabbitMQ简介什么是MQMQ(messagequeue),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信服务。使用了MQ之后,消息发......
  • CentOS7 安装rabbitmq
    参考:原文链接:https://blog.csdn.net/JingleYe/article/details/141959978  第一步、安装任何软件包之前,建议使用以下命令更新软件包和存储库yum-yupdate第二步、Erlang在默认的YUM存储库中不可用,因此您将需要安装EPEL存储库yum-yinstallepel-releaseyum-yupdat......
  • 安装rabbitMQ
    前提:安装docker服务并配置私有库地址云服务器/本地服务器开放rabbitMQ端口:5672和15672拉取镜像#登陆私有库dockerlogin-uadmin-p私有库密码http://8.134.59.62:8083#查看私有库镜像列表curl8.134.59.62:8083/v2/_catalog#拉取镜像dockerpull8.134.59.62:8083/......
  • RabbitMQ的五种模式和四种交换机
    六种消息模式而在的RabbitMQ中,出现了六种消息传播模式:RabbitMQ官网说明的六种模式SimpleWorkQueue(简单工作队列):也就是常说的点对点模式,一条消息由一个消费者进行消费。(当有多个消费者时,默认使用轮训机制把消息分配给消费者)。WorkQueues(工作队列):也叫公平队列,能者多劳......
  • Rabbitmq中的几个关键概念
    在RabbitMQ中,Connection、Channel、Queues(队列)、Exchange(交换机)是几个核心概念,它们各自的含义及之间的关系如下:一、定义与功能Connection(连接)是RabbitMQ的socket链接,封装了socket协议相关部分逻辑。用于生产者、消费者与RabbitMQ服务器进行连接。Channel(通道)是与Rabb......
  • 详解RabbitMQ在Ubuntu上的安装
    ​​​​​​​目录Ubuntu环境安装安装Erlang查看Erlang版本退出命令​编辑安装RabbitMQ确认安装结果安装RabbitMQ管理界面启动服务查看服务状态通过IP:port访问添加管理员用户给用户添加权限再次访问Ubuntu环境安装安装ErlangRabbitMq需要Erlang语⾔的⽀......
  • 将 EX4 TO MQ4 的注意事项和方法
    将`.ex4`文件转换为`.mq4`文件本质上涉及反编译`.ex4`文件,这是一个需要注意的重要问题,1**正确的处理方法**  如果您有修改`.ex4`的需要,可以尝试以下方法:  -**联系开发者**:联系原作者,获取`.mq4`源代码或请求他们进行修改。  -**重新开发**:根据需要功......
  • WindowsRabbitMQ配置
    一、安装Erlang(1)安装地址下载地址:https://www.erlang.org/downloads(2)设置环境变量  新建 ERLANG_HOME (3)修改环境变量path,增加Erlang变量至path ,%ERLANG_HOME%\bin (4)打开cmd命令框,输入erl 二、安装rabbitmq (1)安装地址下载地址:http://www.rabbitmq......