RabbitMq的运用(1)—— 基础使用方法介绍

目录

1.RabbitMQ-异步世界的同步器

1.1.同步调用

1.2.异步调用

2.RabbitMq安装

1.1windows安装

1.2 docker安装

3.网页端操作

3.1登录

 3.2队列

3.3交换机

3.3.1绑定交换机和队列的关系

3.3.2发送消息到队列

3.3.3在队列中查看信息

 3.4更换用户

3.4.1新增账号

3.4.2创建虚拟主机

4.SpringAMQP

4.1向队列发送消息

4.2WorkQueues模型


1.RabbitMQ-异步世界的同步器

1.1.同步调用

我们现在基于多个微服务采用OpenFeign的调用都属于是同步调用,那么这种方式存在哪些问题呢? 举个例子,我们以余额支付功能为例来分析,首先看下整个流程:

目前我们采用的是基于OpenFeign的同步调用,也就是说业务执行流程是这样的:

  • 支付服务需要先调用用户服务完成余额扣减

  • 然后支付服务自己要更新支付流水单的状态

  • 然后支付服务调用交易服务,更新业务订单状态为已支付

三个步骤依次执行。 这其中就存在3个问题: 第一拓展性差 我们目前的业务相对简单,但是随着业务规模扩大,产品的功能也在不断完善。 在大多数电商业务中,用户支付成功后都会以短信或者其它方式通知用户,告知支付成功。假如后期产品经理提出这样新的需求,你怎么办?是不是要在上述业务中再加入通知用户的业务? 某些电商项目中,还会有积分或金币的概念。假如产品经理提出需求,用户支付成功后,给用户以积分奖励或者返还金币,你怎么办?是不是要在上述业务中再加入积分业务、返还金币业务? 。。。 最终你的支付业务会越来越臃肿:

也就是说每次有新的需求,现有支付逻辑都要跟着变化,代码经常变动,不符合开闭原则,拓展性不好。

第二性能下降 由于我们采用了同步调用,调用者需要等待服务提供者执行完返回结果后,才能继续向下执行,也就是说每次远程调用,调用者都是阻塞等待状态。最终整个业务的响应时长就是每次远程调用的执行时长之和:

假如每个微服务的执行时长都是50ms,则最终整个业务的耗时可能高达300ms,性能太差了。

第三,级联失败 由于我们是基于OpenFeign调用交易服务、通知服务。当交易服务、通知服务出现故障时,整个事务都会回滚,交易失败。 这其实就是同步调用的级联失败问题。

但是大家思考一下,我们假设用户余额充足,扣款已经成功,此时我们应该确保支付流水单更新为已支付,确保交易成功。毕竟收到手里的钱没道理再退回去吧

image.png

因此,这里不能因为短信通知、更新订单状态失败而回滚整个事务。

而要解决这些问题,我们就必须用异步调用的方式来代替同步调用

1.2.异步调用

异步调用方式其实就是基于消息通知的方式,一般包含三个角色:

  • 消息发送者:投递消息的人,就是原来的调用方

  • 消息Broker:管理、暂存、转发消息,你可以把它理解成微信服务器

  • 消息接收者:接收和处理消息的人,就是原来的服务提供方

在异步调用中,发送者不再直接同步调用接收者的业务接口,而是发送一条消息投递给消息Broker。然后接收者根据自己的需求从消息Broker那里订阅消息。每当发送方发送消息后,接受者都能获取消息并处理。 这样,发送消息的人和接收消息的人就完全解耦了。

还是以余额支付业务为例:

除了扣减余额、更新支付流水单状态以外,其它调用逻辑全部取消。而是改为发送一条消息到Broker。而相关的微服务都可以订阅消息通知,一旦消息到达Broker,则会分发给每一个订阅了的微服务,处理各自的业务。

假如产品经理提出了新的需求,比如要在支付成功后更新用户积分。支付代码完全不用变更,而仅仅是让积分服务也订阅消息即可:

不管后期增加了多少消息订阅者,作为支付服务来讲,执行问扣减余额、更新支付流水状态后,发送消息即可。业务耗时仅仅是这三部分业务耗时,仅仅100ms,大大提高了业务性能。

另外,不管是交易服务、通知服务,还是积分服务,他们的业务与支付关联度低。现在采用了异步调用,解除了耦合,他们即便执行过程中出现了故障,也不会影响到支付服务。

综上,异步调用的优势包括:

  • 耦合度更低

  • 性能更好

  • 业务拓展性强

  • 故障隔离,避免级联失败

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

  • 完全依赖于Broker的可靠性、安全性和性能

  • 架构复杂,后期维护和调试麻烦

2.RabbitMq安装

1.1windows安装

RabbitMq Windows安装:https://blog.youkuaiyun.com/qq_25919879/article/details/113055350

不想通过百度网盘下载的,可以自行访问官网下载:

Erlang安装官网: https://www.erlang.org/downloads

RabbitMq安装官网: https://www.rabbitmq.com/

安装:rabbitmq-service start

移除:rabbitmq-service remove

启动:rabbitmq-service install

访问路径:http://127.0.0.1:15672/               用户名和密码,默认为guest 

1.2 docker安装

创建网络

 docker network create mq

安装 

docker run \
 -e RABBITMQ_DEFAULT_USER=guest\
 -e RABBITMQ_DEFAULT_PASS=guest\
 -v mq-plugins:/plugins \
 --name mq \
 --hostname mq \
 -p 15672:15672 \
 -p 5672:5672 \
 --network mq\
 -d \
 rabbitmq:3.8-management

访问路径:http://服务器公网ip:15672/                用户名和密码,默认为guest 

5672是API接口

RabbitMQ对应的架构如图:

image.png

其中包含几个概念:

  • **publisher**:生产者,也就是发送消息的一方

  • **consumer**:消费者,也就是消费消息的一方

  • **queue**:队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理

  • **exchange**:交换机,负责消息路由。生产者发送的消息由交换机决定投递到哪个队列。

  • **virtual host**:虚拟主机,起到数据隔离的作用。每个虚拟主机相互独立,有各自的exchange、queue

3.网页端操作

3.1登录

默认情况下 账号密码为guest   默认虚拟主机为 /  

登录默认账号guest

 3.2队列

3.3交换机

我们打开Exchanges选项卡,可以看到已经存在很多交换机,这些都是RabbitMq默认创建的交换机,也可以创建新的交换机

3.3.1绑定交换机和队列的关系

点击Exchanges选项卡,点击amq.fanout交换机,进入交换机详情页,然后点击Bindings菜单,在表单中填写要绑定的队列名称:

 

3.3.2发送消息到队列

3.3.3在队列中查看信息

此时可以看到接收到的消息个数

进入队列hello.queue1,就可以查看接收到的消息 

 3.4更换用户

3.4.1新增账号

创建一个新用户,给予管理员权限

 退出,重新登录

登录后,我们可以发现,之前创建的队列,是拒绝访问的,因为这个队列是创建在虚拟主机“/”中的,而虚拟主机“/”是归guest用户管理的。之所以可以看到这个队列是因为该账号具有管理员权限

 而该账号,没有虚拟主机,不能进行操作

3.4.2创建虚拟主机

为该账号创建虚拟主机

 

创建完成后如图:  

此时,点击页面右上角的virtual host下拉菜单,切换virtual host/hmall:  

然后再次查看queues选项卡,会发现之前的队列已经看不到了 ,这就是基于virtual host 的隔离效果。

4.SpringAMQP

RabbitTemplate是 Spring AMQP 提供的一个核心组件,用于发送和接收消息。RabbitTemplate 提供了一个高层次的抽象来执行 AMQP 操作,例如发布消息到交换机或从队列中接收消息。使用 Spring AMQP,可以使开发者专注于业务逻辑而不是底层的网络通信细节。它促进了松耦合架构的设计,使得系统更易于维护和发展

        <!--AMQP依赖,包含RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
spring:
  rabbitmq:
    host: 127.0.0.1 # 你的虚拟机IP
    port: 5672 # 端口
    virtual-host: /hmall # 虚拟主机
    username: hmall # 用户名
    password: 123 # 密码

4.1向队列发送消息

在发布者和消费者中都需要导入配置文件

发布者负责向队列中发送消息

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);
    }
}

消费者负责监听队列中的消息 

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

 启动程序

4.2WorkQueues模型

Work queues,任务模型。简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息

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

这次我们循环发送,模拟大量消息堆积现象。

    /**
     * workQueue
     * 向队列中不停发送消息,模拟消息堆积。
     */
    @Test
    public void testWorkQueue() throws InterruptedException {
        // 队列名称
        String queueName = "work.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);
}

注意到这两消费者,都设置了Thead.sleep,模拟任务耗时:

  • 消费者1 sleep了20毫秒,相当于每秒钟处理50个消息

  • 消费者2 sleep了200毫秒,相当于每秒处理5个消息  

可以看到消费者1和消费者2竟然每人消费了25条消息:

  • 消费者1很快完成了自己的25条消息

  • 消费者2却在缓慢的处理自己的25条消息。

也就是说消息是平均分配给每个消费者,并没有考虑到消费者的处理能力。导致1个消费者空闲,另一个消费者忙的不可开交。没有充分利用每一个消费者的能力,最终消息处理的耗时远远超过了1秒。这样显然是有问题的。

在spring中有一个简单的配置,可以解决这个问题。我们修改consumer服务的application.yml文件,添加配置:

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

再次测试,可以发现,由于消费者1处理速度较快,所以处理了更多的消息;消费者2处理速度较慢,只处理了6条消息。而最终总的执行耗时也在1秒左右,大大提升。 正所谓能者多劳,这样充分利用了每一个消费者的处理能力,可以有效避免消息积压问题。


下一篇:RabbitMq的运用(2)—— 交换机和消息转换器的使用-优快云博客

### RabbitMQ 尚硅谷教程概述 尚硅谷提供了详尽的 RabbitMQ 教程,涵盖了从基础概念到高级应用的内容。该教程不仅适合初学者快速入门消息队列技术,也适用于希望深入了解 RabbitMQ 架构设计和技术细节的学习者[^1]。 #### 主要章节介绍 #### 安装配置指南 教程首先介绍了如何安装和配置 RabbitMQ 服务器环境,包括依赖软件 Erlang 的安装方法以及通过官方镜像部署 Docker 容器的方式。对于不同操作系统平台下的具体操作步骤均有详细介绍,并附带常见错误排查技巧[^2]。 #### 基础功能讲解 接着深入浅出地解释了 RabbitMQ 的核心组件——交换机(Exchange)、队列(Queue) 和绑定(Binding),并通过实例演示这些基本元素之间的协作关系。这部分还涉及生产者发送消息至指定路由键的过程说明及其工作原理分析[^3]。 #### 高级特性探讨 进一步讨论了诸如持久化存储、事务处理机制、确认模式(Acknowledgment Mode)等更为复杂的主题;同时也分享了一些优化性能的最佳实践案例研究,帮助读者更好地理解和运用 RabbitMQ 提供的强大功能来满足实际项目需求[^4]。 #### 实战演练环节 最后安排了一系列实战练习题目,引导学员亲手构建基于 Spring Boot 或其他框架集成的应用场景,在真实环境中体验并掌握所学知识点。每一步骤都配有详细的命令行提示与代码片段展示,便于跟随学习[^5]。 ```markdown # RabbitMQ 尚硅谷教程 ## 目录结构 - [前言](./preface.md) - [第一章:RabbitMQ简介](./chapter1/introduction.md) - ... - [第二章:安装与配置](./chapter2/installation_configuration.md) - ... - [第三章:基础知识](./chapter3/fundamentals.md) - ... - [第四章:高级特性](./chapter4/advanced_features.md) - ... - [第五章:实战演练](./chapter5/practical_exercises.md) - ... ## 贡献者名单 感谢所有为本教程做出贡献的人们... ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java_学习爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值