MQ入门(一):同步调用和异步调用--RabbitMQ基础入门

news2024/11/16 5:41:32

目录

1.初识MQ

1.1.同步调用

1.2.异步调用

1.3.技术选型

2.RabbitMQ

2.1.安装部署

2.2.RabbitMQ基本架构

2.3.收发消息

2.3.1.交换机

2.3.2.队列

2.3.3.绑定关系

2.3.4.发送消息

2.4.数据隔离

2.4.1.用户管理

2.4.2.virtual host


1.初识MQ

微服务一旦拆分,必然涉及到服务之间的相互调用,目前我们服务之间调用采用的都是基于OpenFeign的调用。这种调用中,调用者发起请求后需要等待服务提供者执行业务返回结果后,才能继续执行后面的业务。也就是说调用者在调用过程中处于阻塞状态,因此我们称这种调用方式为同步调用,也可以叫同步通。但在很多场景下,我们可能需要采用异步通讯的方式,为什么呢?

我们先来看看什么是同步通讯和异步通讯。如图:

- 同步通讯:就如同打视频电话,双方的交互都是实时的。因此同一时刻你只能跟一个人打视频电话。
- 异步通讯:就如同发微信聊天,双方的交互不是实时的,你不需要立刻给对方回应。因此你可以多线操作,同时跟多人聊天。

两种方式各有优劣,打电话可以立即得到响应,但是你却不能跟多个人同时通话。发微信可以同时与多个人收发微信,但是往往响应会有延迟。

所以,如果我们的业务需要实时得到服务提供方的响应,则应该选择同步通讯同步调用)。而如果我们追求更高的效率,并且不需要实时响应,则应该选择异步通讯异步调用)。

同步调用的方法已经学过了,就是OpenFeign。

1.1.同步调用

微服务之间采用OpenFeign调用就属于同步方式,调用可以实时得到结果,但存在下面的问题:

举个例子,以余额支付功能为例来分析,首先看下整个流程:

目前我们采用的是基于OpenFeign的同步调用,也就是说业务执行流程是这样的:
- 支付服务需要先调用用户服务完成余额扣减
- 然后支付服务自己要更新支付流水单的状态
- 然后支付服务调用交易服务,更新业务订单状态为已支付

三个步骤依次执行。
这其中就存在3个问题:

第一扩展性低

我们目前的业务相对简单,但是随着业务规模扩大,产品的功能也在不断完善。
在大多数电商业务中,用户支付成功后都会以短信或者其它方式通知用户,告知支付成功。假如后期产品经理提出这样新的需求,是不是要在上述业务中再加入通知用户的业务?
也就是说每次有新的需求,现有支付逻辑都要跟着变化,代码经常变动,不符合开闭原则,拓展性不好。

第二性能下降

由于我们采用了同步调用,调用者需要等待服务提供者执行完返回结果后,才能继续向下执行,也就是说每次远程调用,调用者都是阻塞等待状态。最终整个业务的响应时长就是每次远程调用的执行时长之和:
假如每个微服务的执行时长都是50ms,则最终整个业务的耗时可能高达300ms,性能太差了。

第三,资源浪费

采用同步调用,调用者在等待响应时,不能释放请求所占用的资源,高并发场景下会极度浪费系统资源;

第四,级联失败

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

大家思考一下,我们假设用户余额充足,扣款已经成功,此时我们应该确保支付流水单更新为已支付,确保交易成功。毕竟收到手里的钱没道理再退回去吧。(doge)
因此,这里不能因为短信通知、更新订单状态失败而回滚整个事务。

综上,同步调用的方式存在下列问题:

扩展性低:每次加入新的需求,都要修改原来的代码;
性能下降:调用者需要等待服务提供者的响应,如果调用链过长则响应时间等于各个调用时间之和;
资源浪费:调用者在等待响应时,不能释放请求所占用的资源,高并发场景下会极度浪费系统资源;
级联失败:如果服务提供者出现问题,则调用者也会出现问题,甚至导致集群故障。

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

1.2.异步调用

异步调用方式其实就是基于消息通知的方式,一般包含三个角色:
- 消息发送者:投递消息的人,就是原来的调用方
- 消息Broker:管理、暂存、转发消息,你可以把它理解成微信服务器
- 消息接收者:接收和处理消息的人,就是原来的服务提供方

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

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

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

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

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

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

综上,异步调用的优势包括:
- 耦合度更低
- 性能更好
- 业务拓展性强
- 故障隔离,避免级联失败

当然,异步通信也并非完美无缺,它存在下列缺点:
- 完全依赖于Broker的可靠性、安全性和性能
- 架构复杂,后期维护和调试麻烦

1.3.技术选型

消息Broker,目前常见的实现方案就是消息队列(MessageQueue),简称为MQ.
几种常见MQ的对比:

根据以上表格,选择哪种MQ的策略可以参考如下:

  • 追求可用性:Kafka、 RocketMQ 、RabbitMQ
  • 追求可靠性:RabbitMQ、RocketMQ
  • 追求吞吐能力:RocketMQ、Kafka
  • 追求消息低延迟:RabbitMQ、Kafka

目前国内消息队列使用最多的还是RabbitMQ,再加上其各方面都比较均衡,稳定性也好。所以我们使用RabbitMQ来学习。

2.RabbitMQ

RabbitMQ是基于Erlang语言开发的开源消息通信中间件。
官网地址:https://www.rabbitmq.com/

2.1.安装部署

基于Docker来安装RabbitMQ,使用下面的命令即可:
(1)在线拉取镜像:

docker pull rabbitmq:3.8-management

(2)创建并允许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 hm-net\
 -d \
 rabbitmq:3.8-management

可以看到在安装命令中有两个映射的端口:
- 15672:RabbitMQ提供的管理控制台的端口
- 5672:RabbitMQ的消息发送处理接口

如果拉取镜像困难的话,直接利用docker load命令加载镜像包:

安装完成后,我们访问 http://192.168.52.135:15672即可看到管理控制台。首次访问需要登录,默认的用户名和密码在配置文件中已经指定了。
登录后即可看到管理控制台总览页面:

至此,单机RabbitMQ部署完毕。

2.2.RabbitMQ基本架构


其中包含几个概念:
- publisher:生产者,也就是发送消息的一方
- consumer:消费者,也就是消费消息的一方
- queue:队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理
- exchange:交换机,负责消息路由。生产者发送的消息由交换机决定投递到哪个队列。
- virtual host:虚拟主机,起到数据隔离的作用。每个虚拟主机相互独立,有各自的exchange、queue

2.3.收发消息

2.3.1.交换机

我们打开Exchanges选项卡,可以看到已经存在很多交换机:

我们点击任意交换机,即可进入交换机详情页面。仍然会利用控制台中的publish message 发送一条消息:


这里是由控制台模拟了生产者发送的消息。由于没有消费者存在,最终消息丢失了,这样说明交换机没有存储消息的能力。

2.3.2.队列

我们打开Queues选项卡,新建一个队列:

命名为hello.queue1

再以相同的方式,创建一个队列,命名为hello.queue2,最终队列列表如下:

此时,我们再次向amq.fanout交换机发送一条消息。会发现消息依然没有到达队列!!

发送到交换机的消息,只会路由到与其绑定的队列,因此仅仅创建队列是不够的,我们还需要将其与交换机绑定。

2.3.3.绑定关系

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

相同的方式,将hello.queue2也绑定到改交换机。

最终,绑定结果如下:

2.3.4.发送消息

再次回到exchange页面,找到刚刚绑定的amq.fanout,点击进入详情页,再次发送一条消息:

回到Queues页面,可以发现hello.queue中已经有一条消息了:

点击队列名称,进入详情页,查看队列详情,这次我们点击get message:

可以看到消息到达队列了:

这个时候如果有消费者监听了MQ的hello.queue1hello.queue2队列,自然就能接收到消息了。

2.4.数据隔离

2.4.1.用户管理

点击Admin选项卡,首先会看到RabbitMQ控制台的用户管理界面:

这里的用户都是RabbitMQ的管理或运维人员。目前只有安装RabbitMQ时添加的itheima这个用户。仔细观察用户表格中的字段,如下:
- Name:itheima,也就是用户名
- Tags:administrator,说明itheima用户是超级管理员,拥有所有权限
- Can access virtual host: /,可以访问的virtual host,这里的/是默认的virtual host

对于小型企业而言,出于成本考虑,我们通常只会搭建一套MQ集群,公司内的多个不同项目同时使用。这个时候为了避免互相干扰, 我们会利用virtual host的隔离特性,将不同项目隔离。一般会做两件事情:
- 给每个项目创建独立的运维账号,将管理权限分离。
- 给每个项目创建不同的virtual host,将每个项目的数据隔离

比如,我们给黑马商城创建一个新的用户,命名为hmall


你会发现此时hmall用户没有任何virtual host的访问权限:

接下来就可以来给他授权

2.4.2.virtual host

我们先退出登录:


切换到刚刚创建的hmall用户登录,然后点击Virtual Hosts菜单,进入virtual host管理页:


可以看到目前只有一个默认的virtual host,名字为 /
我们可以给黑马商城项目创建一个单独的virtual host,而不是使用默认的/


创建完成后如图:

由于我们是登录hmall账户后创建的virtual host,因此回到users菜单,你会发现当前用户已经具备了对/hmall这个virtual host的访问权限了:

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

然后再次查看queues选项卡,会发现之前的队列已经看不到了:

这就是基于virtual host 的隔离效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2155877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web前端字段大小写下划线转换工具

文章目录 前言一、如何使用?二、相关代码总结 前言 程序员在敲代码的过程中都要命名一些字段,但是Java语言对字段的命名规范和sql命名规范不一样,如下图所示,这种机械性的转换工作很劳神费力,为了省点劲写了一个web小…

尚品汇-Jenkins部署构建服务模块、Linux快照备份(五十七)

目录: (1)构建作业(server-gateway) (2)构建service_product模块 (3)演示添加新代码 (4)学会使用linux快照 (1)构建作…

在SpringCloud中实现服务间链路追踪

在微服务架构中,由于系统的复杂性和多样性,往往会涉及到多个服务之间的调用。当一个请求经过多个服务时,如果出现问题,我们希望能够快速定位问题所在。这就需要引入链路追踪机制,帮助我们定位问题。 Spring Cloud为我们…

【沪圈游戏公司作品井喷,游戏产业复兴近在眼前】

近期财报季中,腾讯、网易及B站等国内游戏巨头纷纷亮出亮眼的游戏业务表现,均实现了接近或超越双位数的同比增长。然而,审视过去一年,国内游戏行业仍笼罩在宏观经济“降本增效”的阴影下。 行业数据揭示,全国游戏公司社…

封装 wx.request 的必要性及其实现方式

目录 为什么需要封装 wx.request 1. 避免回调地狱 2. 统一管理 3. 扩展功能 小程序异步 API 的改进 封装实现方式 在小程序开发中,网络请求是不可或缺的功能之一。小程序提供了 wx.request API 来实现网络请求,但直接使用这个 API 在复杂场景下可…

关于SpringBoot项目使用maven打包由于Test引起的无法正常打包问题解决

一、问题描述 在日常工作中,在接手项目时,项目未必是“正常”的,一般平常搭建项目,都不会采用一键式生成的方式,现在说下旧项目,可能项目结构并不是那么简洁,通常都带有与main同层级的test&…

Cpp类和对象(中续)(5)

文章目录 前言一、赋值运算符重载运算符重载赋值运算符重载赋值运算符不可重载为全局函数前置和后置的重载 二、const修饰成员函数三、取地址及const取地址操作符重载四、日期类的实现构造函数日期 天数日期 天数日期 - 天数日期 - 天数日期类的大小比较日期类 > 日期类日…

嵌入式系统stm32cube本地安装出现的问题

stm32cube在线安装很慢,本地安装中出现的一个bug stm32cube_fw_f4_v1281安装成功之后,如果想安装stm32cube_fw_f4_v1281会提示stm32cube_fw_f4_v1280未安装。 如果先安装stm32cube_fw_f4_v1280之后,再安装stm32cube_fw_f4_v1281还会提示这个…

Python模拟鼠标轨迹[Python]

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型,如直线或曲线路径。然而,这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现,能够通过深度学习技术,学习并模拟更自然的鼠标移动行为。 二.鼠标轨迹算法实…

C#如何把写好的类编译成dll文件

1 新建一个类库项目 2 直接改写这个Class1.cs文件 3 记得要添加Windows.Forms引用 4 我直接把在别的项目中做好的cs文件搞到这里来,连文件名也改了(FilesDirectory.cs),这里using System.Windows.Forms不会报错,因为前…

go项目多环境配置

1.java项目配置加载最佳实践 在 Spring Boot 项目中,配置文件的加载和管理是开发过程中不可或缺的一部分。Spring Boot 提供了一套灵活且强大的机制来加载配置文件,使得开发者能够根据不同的环境和需求轻松地管理配置。当多个位置存在相同的配置文件时&…

Python语法进阶之路

一、Python基础 1.1 注释 定义和作用 对代码解释说明,增强可读性 单行注释 # 多行注释 """ 这是一个多行注释 """ 1.2 变量及变量类型 定义和作用 计算机目的是计算,编程是为了更方便计算,计算对象就是…

vue循环渲染动态展示内容案例(“更多”按钮功能)

当我们在网页浏览时,常常会有以下情况:要展示的内容太多,但展示空间有限,比如我们要在页面的一部分空间中展示较多的内容放不下,通常会有两种解决方式:分页,“更多”按钮。 今天我们的案例用于…

mybatis 配置文件完成增删改查(二):根据条件查询一个

文章目录 参数占位符#{}:会将其替换为? ——为了防止sql注入${}:会将其替换为实际接收到的数据,拼sql ——无法防止sql注入 查询一个sql特殊字符的处理 参数占位符 #{}:会将其替换为? ——为了防止sql注入 ${}:会将其替换为实际接收到的数据…

Java继承教程!(o|o)

Java 继承 Java面向对象设计 - Java继承 子类可以从超类继承。超类也称为基类或父类。子类也称为派生类或子类。 从另一个类继承一个类非常简单。我们在子类的类声明中使用关键字extends,后跟超类名称。 Java不支持多重继承的实现。 Java中的类不能有多个超类。…

Linux-gcc/g++

系列文章目录 C语言中的编译和链接 文章目录 系列文章目录一、编译过程gcc如何完成过程在这里涉及到一个重要的概念:函数库 二、动态库、静态库2.1 函数库一般分为静态库和动态库两种。 三、gcc选项gcc选项记忆 一、编译过程 具体过程在这一片c语言文章中讲解过:C语言中的编…

shardingjdbc分库分表原理

一 Mysql的瓶颈 二 解决方案 三 hash环算法 四 雪花算法

CCF csp认证 小白必看

c支持到C17(还是更高?);所以学一些封装好的函数功能是必要的---比如STL里的函数; 因为可携带纸质资料,建议打印带入,需要时可翻阅。 【题目概述:】 0-devc环境配置 配置好你常用的编译版本: 想要调试记得开启下选…

分布式系统的CAP原理

CAP 理论的起源 CAP 理论起源于 2000 年,由加州大学伯克利分校的 Eric Brewer 教授在分布式计算原理研讨会(PODC)上提出,因此 CAP 定理又被称作布鲁尔定理(Brewer’s Theorem)。2002 年,麻省理…

「OC」引用计数(一)

iOS学习 前言自动引用计数引用计数引用计数的思考方式自己生成的对象,自己持有非自己生成的对象,自己也能持有不再需要自己持有的对象时释放无法释放非自己持有的对象 总结 前言 在学习oc时对引用计数略有了解,现在进行系统的学习总结。 自动…