RabbitMQ实战

文章目录

1、简介

素材:链接:https://pan.baidu.com/s/1YjVM9WBEIVCbYZmlzowyKw?pwd=lpkz

官网:https://www.rabbitmq.com/

RabbitMQ 是一个开源的消息队列中间件,采用 Erlang 语言编写,支持多种消息协议,如 AMQP、MQTT、STOMP 等。它可以作为消息的中转站,在分布式系统中协调不同组件之间的数据传输,实现松耦合的系统架构。

2、MQ

优点

  1. 灵活的路由方式,支持消息的广播、点对点、主题订阅等多种路由方式;
  2. 异步处理消息,提高系统的并发性能;
  3. 持久化机制,保证在服务器宕机、重启等情况下消息的可靠性;
  4. 高可用和负载均衡,支持集群和镜像模式,提供高可用和负载均衡的目标;
  5. 插件机制,支持丰富的插件,如认证授权、可视化管理等。

缺点

  1. 系统可用性降低: 系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。
  2. 系统复杂度提高: MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。
  3. 一致性问题 : A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败,则会造成数据处理的不一致。

MQ的应用场景

  1. 高峰流量:抢红包、秒杀活动、抢火车票等这些业务场景都是短时间内需要处理大量请求,如果直接连接系统处理业务,会耗费大量资源,有可能造成系统瘫痪。 而使用MQ后,可以先让用户将请求发送到MQ中,MQ会先保存请求消息,不会占用系统资源,且MQ会进行消息排序,先请求的秒杀成功,后请求的秒杀失败。
  2. 消息分发:如电商网站要推送促销信息,该业务耗费时间较多,但对时效性要求不高,可以使用MQ做消息分发。
  3. 数据同步:假如我们需要将数据保存到数据库之外,还需要一段时间将数据同步到缓存(如Redis)、搜索引擎(如Elasticsearch)中。此时可以将数据库的数据作为消息发送到MQ中,并同步到缓存、 搜索引擎中。
  4. 异步处理:在电商系统中,订单完成后,需要及时的通知子系统(进销存系统发货,用户服务积分,发送短信)进行下一步操作。为了保证订单系统的高性能,应该直接返回订单结果,之后让MQ通知子系统做其他非实时的业务操作。这样能保证核心业务的高效及时
  5. 离线处理:在银行系统中,如果要查询近十年的历史账单,这是非常耗时的操作。如果发送同步请求,则会花费大量时间等待响应。此时使用MQ发送异步请求,等到查询出结果后获取结果即可。

AMQP

1、什么是 AMQP : 即Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,专门为消息中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受不同中间件产品,不同开发语言等条件的限制。2006年AMQP规范发布,类比HTTP。

2、AMQP工作过程: 生产者(Publisher)将消息发布到交换机(Exchange),交换机根据规则将消息分发给交换机绑定的队列(Queue),队列再将消息投递给订阅了此队列的消费者

工作原理

  1. Producer【消息的生产者】 一个向交换机发布消息的客户端应用程序。
  2. Connection 【连接】 生产者/消费者和RabbitMQ服务器之间建立的TCP连接。
  3. Channel【信道】 是TCP里面的虚拟连接。例如:Connection相当于电缆,Channel相当于独立光纤束,一条TCP连接中可以创建多条信道,增加连接效率。无论是发布消息、接收消息、订阅队列都是通过信道完成的。
  4. Broker 消息队列服务器实体。即RabbitMQ服务器
  5. Virtual Host【虚拟主机】 出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中。每个vhost本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列、交换机、绑定和权限机制。当多个不同的用户使用同一个RabbitMQ服务器时,可以划分出多个虚拟主机。RabbitMQ默认的虚拟主机路径是 /
  6. Exchange 【交换机】 用来接收生产者发送的消息,并根据分发规则,将这些消息分发给服务器中的队列中。不同的交换机有不同的分发规则。
  7. Queue【消息队列】 用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。消息一直在队列里面,等待消费者链接到这个 队列将其取走。
  8. Binding 【绑定】 消息队列和交换机之间的虚拟连接,绑定中包含路由规则,绑定信息保存到交换机的路由表中,作为消息的分发依据。
  9. Consumer【消息的消费者】 表示一个从消息队列中取得消息的客户端应用程序。

市面上常见的MQ

在这里插入图片描述

3、Linux安装RabbitMQ

安装rabbitmq分3个步: 1、先安装socat, ——》2、安装erlang, ——》3、安装rabbitmq-server。

3.1 版本对应

网址:https://www.rabbitmq.com/which-erlang.html

3.2 安装socat

命令:yum -y install socat

3.3 下载 Erlang/OTP、安装、验证 erlang

在这里插入图片描述

官网:下载 - Erlang/OTP

方法一:

1. 下载

在这里插入图片描述

2. 将下载的Erlang服务上传到服务器上面
cd /home
mkdir /home/rabbitMQ
cd /home/rabbitMQ

在这里插入图片描述

在这里插入图片描述

3. 解压
tar -zvxf otp_src_24.0.tar.gz

在这里插入图片描述

4. 编译erlang的依赖环境

跟大家讲一下,erlang依赖的环境特别特别多,就拿gcc来说,如果以前安装过这个环境还不止,所以我们重新安装一下也无所谓所以我们执行以下的命令:

解压成功,安装编译所需要的依赖文件

yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel unixODBC-devel

在这里插入图片描述

等待安装完毕

创建Erlang文件夹

mkdir /home/rabbitMQ/erlang
cd /home/rabbitMQ/otp_src_24.0

在这里插入图片描述

然后执行下面的命令

./configure  --prefix=/home/rabbitMQ/erlang --without-javac

在这里插入图片描述

5. 安装Erlang

make : 编译
make install : 安装
&& : 前面的命令执行成功后面的命令才会执行

make && make install

在这里插入图片描述

6. 配置Erlang环境
vi /etc/profile

在这里插入图片描述

加入

#set erlang environment
export ERLANG_HOME=/home/rabbitMQ/erlang
export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:${ERLANG_HOME}/bin:$PATH

在这里插入图片描述

按Esc键 输入 :wq (退出并保存) :q! (退出不保存)

刷新配置文件在这里插入图片描述

7. 测试Erlang是否安装成功
输入命令: erl

在这里插入图片描述

如图所示说明已经安装成功了!!

方法二:

1. 下载
下载命令: sudo yum install erlang

在这里插入图片描述

2. 安装

接着上一步, 继续回复“y”,
提示:见到Complete!(成功),表示安装erlang 成功了。

在这里插入图片描述

3. 验证 erlang 是否安装成功。

命令: yum info erlang
提示: erlang 的版本信息、软件网址、占用大小空间等,就表示安装成功了。

在这里插入图片描述

4. 卸载 erlang(遇到下载的erlang与rabbitmq-server 版本冲突)

执行3条命令

   yum list | grep erlang
   yum -y remove erlang-*
   yum remove erlang.x86_64
5. 重新安装 erlang 和验证 erlang

安装已经下载好的erlang包, 文件路径 ./rabbitMQ/ 文件下
安装命令: rpm -ivh erlang-23.3-2.el8.x86_64.rpm
验证erlang命令: yum info erlang

3.4 安装、验证rabbitmq-server(rabbitMQ服务器)

注意:需要下载Linux版本的

官网:https://www.rabbitmq.com/

在RabbitMQ官网可以看到RabbitMQ对应的Erlang版本

1. 下载RabbitMQ

在这里插入图片描述

2. 将RabbitMQ上传到服务器

cd /home/rabbitMQ

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

3. 解压RabbitMQ服务

根据压缩包后缀不同使用不同的命令进行解压

xz -d rabbitmq-server-generic-unix-latest-toolchain-3.9.5.tar.xz
tar -xvf rabbitmq-server-generic-unix-latest-toolchain-3.9.5.tar

在这里插入图片描述

4. 配置环境变量

vi /etc/profile

在这里插入图片描述

加入

#set rabbitmq environment
export RABBITMQ=/home/rabbitMQ/rabbitmq_server-3.9.5
export PATH=$PATH:${RABBITMQ}/sbin

在这里插入图片描述

按Esc键 输入 :wq (退出并保存) :q! (退出不保存)

刷新配置文件

source /etc/profile

在这里插入图片描述

5. 开启web管理插件

cd /home/rabbitMQ/rabbitmq_server-3.9.5/sbin
./rabbitmq-plugins enable rabbitmq_management  # 启动指定的插件:

在这里插入图片描述

启动插件成功

在这里插入图片描述

6. 启动RabbitMQ服务

ls
./rabbitmq-server -detached    # 以守护进程启动

在这里插入图片描述

7. 访问RabbitMQ管理界面

浏览器访问:http://IP:15672

看到如下这个界面就是正常启动了

在这里插入图片描述

8. 设置允许远程访问

从上面截图可以看到使用guest登录,提示“User can only log in via localhost”,无法登录,原因是3.3.0版本后禁止用户在除locahost外的访问,只能通过本地主机登录。

方法一——新加用户

新加个用户,设置权限,设置角色。

  1. rabbitmqctl add_user admin admin:这个命令是用来添加一个新的RabbitMQ用户这个命令将创建一个名为admin的用户,并设置其密码为admin请注意,这两个参数(用户名和密码)在你的问题中是硬编码的,这在实际生产环境中并不安全,建议使用更复杂和随机化的用户名和密码
  2. rabbitmqctl set_permissions -p / admin ".*" ".*" ".*":这个命令是设置用户admin在RabbitMQ的权限这里的-p /参数表示设置的是全局权限而".*" ".*" ".*"表示赋予admin用户所有权限,包括配置权限、写权限和读权限
  3. rabbitmqctl set_user_tags admin administrator:这个命令是为用户admin添加了一个标签(或者权限等级)在这个例子中,添加的是administrator标签这个命令可能不是必要的,因为RabbitMQ通常不会直接使用这种用户标签
rabbitmqctl add_user admin admin
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
rabbitmqctl set_user_tags admin administrator

登录成功

在这里插入图片描述

方法二——设置guest

/home/rabbitMQ/rabbitmq_server-3.9.5/plugins/rabbit-3.9.5/ebin目录下找到rabbit.app文件 (find / -name rabbit.app),修改参数。

{loopback_users, [<<"guest">>]}, 修改成{loopback_users, []},

在这里插入图片描述

重启服务

rabbitmqctl stop #停止RabbitMQ 
cd /home/rabbitMQ/rabbitmq_server-3.9.5/sbin
./rabbitmq-server -detached # 以守护进程启动RabbitMQ

使用guest账号登录成功

在这里插入图片描述

4、RabbitMQ实战

4.1 什么是消息队列

MQ全称为Message Queue,即消息队列。”消息队列”是在消息的传输过程中保存消息的容器。它是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦

下图中Producer为生产者,Queue为消息队列,Consumer为消费者

在这里插入图片描述

4.2 RabbitMQ简介

RabbitMQ是一个开源的消息中间件,它实现了高效、可靠的消息传递机制,主要用于应用程序之间的异步通信。它基于AMQP(高级消息队列协议)规范设计,支持多种编程语言,并提供了丰富的特性和灵活的架构。

RabbitMQ的工作原理是利用队列来存储消息,并通过发布-订阅模式实现消息的发送和接收。在这个模式下,消息的发送者将消息发布到一个交换器,交换器根据预定义的规则将消息路由到一个或多个队列,然后消息的接收者从队列中订阅并消费这些消息。

4.3 消息队列应用场景

1. 任务异步处理:

高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达MySQL,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。通过使用消息队列,我们可以异步处理请求,从而缓解系统的压力。将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。减少了应用程序的响应时间。

2. 应用程序解耦合:

MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合

4.4 RabbitMQ的工作原理

在这里插入图片描述

1. 组成部分说明:

· Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue

· Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过滤。

· Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的接受者

· Producer:消息生产者,即生产方客户端,生产方客户端将消息发送给消息队列

· Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。

2. 生产者发送消息流程:

1、生产者和Broker建立TCP连接。

2、生产者和Broker建立Channel通道(信道)。

3、生产者通过Channel通道(信道)把消息发送给Broker,由Exchange将消息进行转发。

4、Exchange将消息转发到指定的Queue(队列)

3. 消费者接收消息流程:

1、消费者和Broker建立TCP连接

2、消费者和Broker建立Channel通道(信道)

3、消费者监听指定的Queue(队列) (每个队列都有一个名字)

4、当有消息到达Queue时Broker默认将消息推送给消费者。

5、消费者接收到消息。

6、ack(消息确认机制)回复

4.5 六种工作模式

RabbitMQ有六种工作模式:基本消息模式、work消息模式、Publish/subscribe (交换机类型:Fanout,也称为广播模式)、Routing 路由模型(交换机类型:direct)、Topics 通配符模式(交换机类型:topics)、RPC

​ 我们这里给大家重点介绍基本消息模式, Routing路由模式(重点)Topic通配符模式(重点)

4.5.1 基本消息模式(简单消息模式)

在这里插入图片描述

在上图的模型中,有以下概念:

P:生产者,也就是要发送消息的程序

C:消费者:消息的接受者,会一直等待消息到来。

queue:消息队列,图中红色部分。可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。

1.1 案例实战
1、 新建一个maven工程

根据下面的步骤建立maven项目

在这里插入图片描述

2、 添加依赖
  	<!--rabbitmq依赖-->
    <dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.7.1</version>
    </dependency>
    <!--再额外添加slf4j的依赖-->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.7.25</version>
      <scope>compile</scope>
    </dependency>
3、 再到java目录下创建org.example.util包,在此包下创建连接工具类:
public class ConnectionUtil {
   
   
    /**
     * 建立与RabbitMQ的连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
   
   
        //定义连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址 (因为rabbitmq安装到linux上面,这里填写linux的IP地址)
        factory.setHost("192.168.181.128");
        //端口
        factory.setPort(5672);
        //设置账号信息,用户名、密码(rabbitmq的用户名和密码)
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 通过工厂获取连接
        Connection connection = factory.newConnection();
        return connection;
    }
}
4、 生产者发送消息
4.1 在org.example.simple包下创建Send类,用于生产者发送消息。
public class Send {
   
   
    private final static String QUEUE_NAME = "simple_queue"; // 队列名
    public static void main(String[] argv) throws Exception {
   
   
        // 1、获取到连接
        Connection connection = ConnectionUtil.getConnection();
        // 2、从连接中创建通道,使用通道才能完成消息相关的操作
        Channel channel = connection.createChannel();
        // 3、声明(创建)队列
        //参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
        /**
         * 参数明细
         * 1、queue 队列名称
         * 2、durable 是否持久化,如果持久化,mq重启后队列还在
         * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
         * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
         * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 4、消息内容
        String message = "Hello World!";
        // 向指定的队列中发送消息
        //参数:String exchange, String routingKey, BasicProperties props, byte[] body
        /**
         * 参数明细:
         * 1、exchange,交换机,如果不指定将使用mq的默认交换机(设置为"")
         * 2、routingKey,路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认交换机,routingKey设置为队列的名称
         * 3、props,消息的属性
         * 4、body,消息内容
         */
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");
        //关闭通道和连接(资源关闭最好用try-catch-finally语句处理)
        channel.close();
        connection.close();
    }
}

4.2 运行上述main方法,在控制台打印信息如下所示:

在这里插入图片描述

4.3 打开浏览器访问:http://IP:15672

web管理页面:服务器地址/端口号 默认用户及密码:guest,如果没有配置可根据 设置允许远程访问中进行用户名密码配置

在这里插入图片描述

4.4 如下图点击Queues,可以在队列列表中可以看到名为simple_queue的队列。

在这里插入图片描述

4.5 点击队列名称simple_queue,进入详情页 —>Get messages,可以查看消息:

在这里插入图片描述

5、消费者接收消息
5.1 在org.example.simple包下创建Receiver类,用于消费者接收消息
public class Receiver{
   
   
    private final static String QUEUE_NAME = "simple_queue";  //队列名
    public static void main(String[] argv) throws Exception {
   
   
        // 获取到连接
        Connection connection = ConnectionUtil.getConnection();
        //创建会话通道,生产者和mq服务所有通信都在channel通道中完成
        Channel channel = connection.createChannel();
        // 声明队列
        //参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
        /**
         * 参数明细
         * 1、queue 队列名称
         * 2、durable 是否持久化,如果持久化,mq重启后队列还在
         * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
         * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
         * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //实现消费方法
        DefaultConsumer consumer = new DefaultConsumer(channel){
   
   
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
            /**
             * 当接收到消息后此方法将被调用
             * @param consumerTag  消费者标签,用来标识消费者的,在监听队列时设置channel.basicConsume
             * @param envelope 信封,通过envelope
             * @param properties 消息属性
             * @param body 消息内容
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
   
   
                // body 即消息体
                String msg = new String(body,"utf-8");
                System.out.println(" [x] received : " + msg + "!");
            }
        };
        // 监听队列,第二个参数:是否自动进行消息确认(用于监听queue队列中是否收到了消息,如果收到消息自动调用上面DefaultConsumer进行默认消费)。
        //参数:String queue, boolean autoAck, Consumer callback
        /**
         * 参数明细:
         * 1、queue 队列名称
         * 2、autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,如果将此参数设置为true表示会自动回复mq,如果设置为false要通过编程实现回复
         * 3、callback,消费方法,当消费者接收到消息要执行的方法
         */
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}
5.2 运行上述main方法,在控制台打印信息如下:

在这里插入图片描述

5.3 打开浏览器访问:http://IP:15672

web管理页面:服务器地址/端口号 默认用户及密码:guest,如果没有配置可根据 设置允许远程访问中进行用户名密码配置

在这里插入图片描述

5.4 再看看队列的消息,已经被消费了,Ready值为0,Total值也为0了。

在这里插入图片描述

我们发现,消费者已经获取了消息,但是程序没有停止,一直在监听队列中是否有新的消息。一旦有新的消息进入队列,就会立即打印

1.2 消息确认机制ACK

通过刚才的案例可以看出,消息一旦被消费者接收,队列中的消息就会被删除。

那么问题来了:RabbitMQ怎么知道消息被接收了呢?

如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是RabbitMQ无从得知,这样消息就丢失了

因此,RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:

Ø 自动ACK:消息一旦被接收,消费者自动发送ACK

Ø 手动ACK:消息接收后,不会发送ACK,需要手动调用

大家觉得哪种更好呢?

这需要看消息的重要性:

Ø 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便

Ø 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。

之前的测试都是自动ACK的,如果要手动ACK,需要改动我们的代码。

1、 在org.example.simple包下创建ACKReceiver类,用于消费者接收消息
public class ACKReceiver {
   
   
    private final static String QUEUE_NAME = "simple_queue";
    public static void main(String[] argv) throws Exception {
   
   
        // 获取到连接
        Connection connection = ConnectionUtil.getConnection();
        // 创建通道
        final Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
   
   
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
   
   
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [x] received : " + msg + "!");
                // 手动进行ACK
                /*
                 *  void basicAck(long deliveryTag, boolean multiple) throws IOException;
                 *  deliveryTag:用来标识消息的id
                 *  multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 监听队列,第二个参数false,手动进行ACK
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}

ACKReceiver类与Receiver类最大的区别就是在消息消费的时候添加了channel.basicAck(envelope.getDeliveryTag(), false);channel.basicConsume(QUEUE_NAME, false, consumer);

2、自动ACK存在的问题
2.1 修改消费者Receiver类的代码

因为Receiver类是采用自动ACK,在handleDelivery方法中添加异常,如下:

在这里插入图片描述

2.2 生产者Send类不做任何修改,直接运行Send类中的main方法

消息发送成功,再访问到RabbitMQ的web界面(之前启动的Receiver消费者要停掉服务),

在这里插入图片描述

2.3 运行Receiver类消费者中的main方法,程序抛出异常:

在这里插入图片描述

2.4再查看rabbitmq的web管理界面:

在这里插入图片描述

消费者抛出异常,但是消息依然被消费,实际上我们还没获取到消息。

3、演示手动ACK

注意:先把Receiver消费者服务停止掉

3.1 重新运行生产者Send中的main方法,实现发送消息,

消息发送成功后,再次查看web管理界面,效果如下所示,队列中收到消息一条。

在这里插入图片描述

3.2 再修改ACKReceiver类中的handleDelivery方法

增加如下图红框里的异常代码(模拟手动进行ack前抛出异常)。

在这里插入图片描述

3.3 再运行ACKReceiver类中的main方法,程序抛出异常:

在这里插入图片描述

3.4 查看web管理页面:

在这里插入图片描述

消息没有被消费掉

这是因为虽然我们设置了手动ACK,但是代码中并没有进行消息确认!所以消息并未被真正消费掉

4、最后消息确认机制的正确做法
4.1 我们要在监听队列时设置第二个参数为false,代码中手动进行ACK

代码如下图红框所示(之前异常的代码需要注释掉)

在这里插入图片描述

4.2 最后运行ACKReceiver类中的main方法,查看web管理页面

在这里插入图片描述

在这里插入图片描述

消费者消费成功!

生产者避免数据丢失:https://www.cnblogs.com/vipstone/p/9350075.html

4.5.2 work工作队列模式

工作队列或者竞争消费者模式

在这里插入图片描述

work queues与入门程序(基本消息模式)相比,多了一个消费端,两个消费端共同消费同一个队列中的消息,但是一个消息只能被一个消费者获取

这个消息模型在Web应用程序中特别有用,可以处理短的HTTP请求窗口中无法处理复杂的任务。

接下来我们来模拟这个流程:

P:生产者:任务的发布者

C1:消费者1:领取任务并且完成任务,假设完成速度较慢(模拟耗时)

C2:消费者2:领取任务并且完成任务,假设完成速度较快

2.1 案例实战
1、生产者

在org.example.work包中创建Send类,生产者循环发送50条消息

public class Send {
   
   
    private final static String QUEUE_NAME = "test_work_queue";
    public static void main(String[] argv) throws Exception {
   
   
        // 获取到连接
        Connection connection = ConnectionUtil.getConnection();
        // 获取通道
        Channel channel = connection.createChanne
### RabbitMQ 实战应用指南 RabbitMQ 是一种流行的消息中间件,广泛应用于分布式系统中,以实现异步处理、应用解耦、流量削峰和日志处理等场景。以下是 RabbitMQ 的一些实战应用指南和使用技术指导。 #### 1. 异步处理 RabbitMQ 可用于实现异步任务处理,从而提高系统的响应速度和吞吐量。例如,在一个电商平台中,订单创建后可以将后续的邮件通知、库存更新等操作通过 RabbitMQ 异步执行,避免阻塞主业务流程。这种模式能够显著提升系统的并发处理能力[^1]。 #### 2. 应用解耦 通过 RabbitMQ,生产者和消费者之间可以解耦,即生产者不需要关心消费者的具体实现,只需要将消息发送到队列中即可。这种方式使得系统模块之间更加松耦合,便于维护和扩展。例如,在一个微服务架构中,服务之间可以通过 RabbitMQ 进行通信,而不必直接调用彼此的接口[^1]。 #### 3. 流量削峰 在高并发场景下,RabbitMQ 可以作为缓冲区,平滑突发的流量高峰。例如,在秒杀活动中,用户请求会被暂存到队列中,后端服务可以按照自身的处理能力逐步消费这些请求,从而避免系统过载[^1]。 #### 4. 日志处理与任务调度 RabbitMQ 还可以用于日志收集和任务调度。例如,多个服务节点可以将日志信息发送到 RabbitMQ,然后由专门的日志处理服务从队列中消费并进行分析。此外,RabbitMQ 也可以用于分布式任务调度,确保任务在多个工作节点上均匀分配。 #### 5. 集群搭建 为了提高 RabbitMQ 的可用性和可靠性,通常需要搭建集群环境。RabbitMQ 支持多种集群模式,包括默认集群模式和镜像集群模式。在默认集群模式下,节点之间共享元数据,但队列数据仅存在于创建它的节点上。而在镜像集群模式下,队列的数据会在多个节点上复制,提供更高的可用性。安装 RabbitMQ 集群时,需要确保节点之间的网络互通,并配置 Erlang Cookie 以保证节点间的通信安全。 #### 6. 工作模式 RabbitMQ 提供了多种工作模式,适用于不同的业务场景: - **简单队列模式**:适用于单个生产者和单个消费者的场景。 - **工作队列模式**:适用于多个消费者竞争消费任务的场景,常用于负载均衡。 - **发布订阅模式**:适用于广播消息给所有消费者。 - **路由模式**:通过绑定键(Routing Key)选择性地将消息发送给特定的消费者。 - **主题模式**:通过通配符匹配绑定键,实现更灵活的消息路由[^1]。 #### 7. 高级特性 RabbitMQ 的一些高级特性可以帮助构建更健壮的消息传递系统: - **消息持久化**:通过将队列和消息设置为持久化,确保即使 RabbitMQ 重启,消息也不会丢失。 - **消息确认机制**:消费者在处理完消息后,需要显式地向 RabbitMQ 发送确认信号,否则消息会被重新投递给其他消费者。 - **死信队列与延迟队列**:死信队列用于处理无法被正常消费的消息,而延迟队列则允许消息在一定时间后才被投递[^1]。 #### 8. 性能优化 为了提升 RabbitMQ 的性能,可以采取以下优化策略: - **队列设计优化**:合理设计队列的数量和结构,避免过多的队列导致性能下降。 - **连接与信道管理**:复用连接和信道,减少网络开销。 - **集群优化配置**:根据业务需求调整集群的配置参数,如内存限制、磁盘空间等。 #### 9. 集成 Spring Boot 在 Spring Boot 项目中集成 RabbitMQ 非常方便,可以通过 `spring-boot-starter-amqp` 依赖快速实现消息的发送与接收。首先需要配置 RabbitMQ 的连接信息,包括主机地址、端口、用户名和密码。然后定义队列、交换机和绑定关系,并编写生产者和消费者的代码。 ```java @Configuration public class RabbitMQConfig { @Bean public Queue myQueue() { return new Queue("myQueue"); } @Bean public DirectExchange myExchange() { return new DirectExchange("myExchange"); } @Bean public Binding binding(Queue myQueue, DirectExchange myExchange) { return BindingBuilder.bind(myQueue).to(myExchange).with("myKey").noargs(); } } ``` #### 10. 跨平台通信 RabbitMQ 还可以作为跨平台通信的中间件,提供统一的消息传输协议和接口。例如,在一个物联网系统中,设备端和服务器端可以通过 RabbitMQ 进行通信,设备端将数据发送到队列中,服务器端从队列中读取并处理数据,从而实现无缝通信[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值