RabbitMQ基础--java客户端

快速入门

对于在java项目中使用之前所介绍的RabbitMQ,我们可以使用Spring AMQP来使用

接下来用一个简单是小实例来快速入门Spring AMQP的使用:

注意:我们之前介绍调用者发送消息先要经过交换机,这里为了简化,因此省去了交换机,直接把消息发送到队列中,我们正常的异步调用还是摇发到交换机中

接下来我们来一一实现需求逐步简单学习吧

创建队列

直接在控制台完成创建即可,这里不多赘述

发送消息

AMQP依赖注入

只要要使用AMQP的微服务就要注入相关依赖,因此直接在这些微服务的父工程注入即可

配置文件

让我们的AMQP可以连接上我们之前部署在虚拟机的RabbitMQ

这里的host写你配置虚拟机RabbitMQ的IP地址,这里我直接在dockerdesktop上配置的,因此直接写localhost也可以连接上

消息发送

配置完成后我们来消息的发送,消息的发送涉及到非常重要的一个工具类"rabbitTemplete",回想我们之前学习的同步调用,除了OpenFeign在这之前我们也学习过"RestTemplete",因此这是个对调用/发送消息重要的类别,这里由于是RabbitMQ操作,那我们就用rabbitTemplete

注入对象后,直接调用它的相关方法即可,方法需要传入队列名词和发送消息的内容,队列名词就是我们前面创建的名词,发送消息我们自己编辑即可

回到rabbitMQ控制台,查看相关队列受到的消息,发现成功发送到队列中

接收消息

前面的步骤依赖注入以及配置文件和发送消息一样,这里不过多赘述

新建类,然后把该类注册到容器中

为该类新增一个监听的方法,方法上需要使用"@RabbitListener"注解标注,这样就可以实时监听相关虚拟主机以及监听队列中收到的消息了

我们再次运行刚刚的测试方法来发送消息

可以看到成功的监听到消息

WorkQueue工作模型

介绍

当多个消费者监听同一个队列,这样我们就称作为"WorkQueue"模型

我们通过一个简单的小实例来看一下该模型下对于消息的分配监听是怎么样的

创建好队列后我们发送50条消息,并且每一条消息带有编号

写两个方法,各自监听同一条队列work.queue,让他们的输出方式稍微不同,一个out,一个err,这样输出就会有颜色区分

测试结果如下(太长就没有放完)

可以看到,在这种模型下消费者平均的接收来自队列中的每一条消息,他们处理完成的时间也差不多

若有多个消费者,那么这样处理大量消息时间会更短,处理速度会更快

优化--能者多劳

背景

在上面的简单介绍我们可以了解到,这种工作模型下每一个消费者都是"非常平均"的监听处理队列中的每一条消息

但是我们在实际应用开发时用到的workQueue的实现和我们刚刚的有所不同,虽然多个消费者监听同一个队列确实可以对起任务效率做到有效提升,但是实际上并不会像这样用两个方法去监听同一个队列,因为这样两个方法还是在同一台机器上运行,占用的资源也是同一台机器的资源,非常容易导致资源的耗尽 ,因此实现WorkQueue的方式一般每台机器都是使用一个方法,然后多台机器集群的方式来监听同一个队列

这就引出一个问题,不同的机器可能性能是完全不同的,有些处理能力快,有些处理能力非常慢,如果还像刚刚那样"完全平均"监听消息就会导致:处理快的机器一下子把任务做完了,资源放在那里纯浪费,处理慢的机器一直在慢慢的处理队列中的一条条消息,用户体验很不好,因此我们必须优化这一点,改变"完全平均"的监听方式,让处理能力强的机器不要浪费资源,继续从队列中监听消息处理

消费者消息推送限制

为了优化刚刚提及的问题,我们可以使用该消费者消息推送限制,在yml文件中修改相关配置,让每一个消费者每次只能获取一条消息,只有处理完该消息后才能获取下一条消息,避免出现消息堆积

我们来实践一下

我们在每一个监听方法后面让他们休眠不同时间,模拟机器处理能力的不同

配置好消费者消息推送机制

再次测试发送50条信息,我们查看结果:

我们发现,这次不再是"完全平均"了,而是能者多劳,消息处理快的机器处理了更多的消息

交换机

我们上文的介绍的内容中一直少了一个重要角色--"交换机",上文是为了介绍方便,我们真实的业务场景下,交换机是必不可少的角色,交换机根据他们路由转发的机制不同,也被分成了不同的类型,下面我们就来介绍各个交换机

Fanout交换机--广播

fanout交换机也称作"广播交换机",我们知道,交换机的作用就是把调用者的消息路由转发给队列,fanout交换机它的路由方式就和名字一样"广播",他会把调用者发送的消息完全复制好几份分别发送给它所管理的不同队列

你可能会疑问,完全一样的消息发好几份有什么意义或者要在什么场景下才能用到呢?

我们来回顾我们之前有关"支付"微服务的场景,如果没有"广播交换机",纯用调用者发送给队列的形式;一个队列被多个消费者监听,这些消费者是不同的微服务,比如说扣钱服务、积分服务、订单服务.....,我们前文又所知像这样子的工作模型,他们处理完消息后消息就直接被释放掉了,那就会出现一种情况,当调用者发出了"申请扣钱"服务的消息,那么很有可能这一条消息被作为消费者的"积分服务"所接收,积分微服务并不会执行相关业务,并且他又接收到了这条消息,那么这条消息被接收后就直接释放丢失,并且也没有完成相关业务

因此为了解决这种情况,我们就可以使用"广播交换机",让其把完全一样的消息发送给多个队列 ,每一个队列都专门负责不同的业务,比如说队列1专门负责扣钱,队列2专门负责积分,这样即使不会执行相关不符合业务,也不会漏掉它该执行的业务

我们来通过一个简单实例来实践一下:

这里注意,创建交换机的时候不要忘记选择类型

编写相关方法

这里注意:由于我们要发送信息给交换机,因此参数不能填两个了,这样首个参数会被默认识别为队列名,我们填入三个参数,这样第一个参数就是交换机名,第二个参数我们填空/null即可

让两个不同的消费者监听不同队列

我们发送消息,查看结果,可以清楚的看到两个队列的消费者都接收到同一条消息

Direct交换机--定向

前面我们介绍了广播交换机,但是他只能把消息广播出去给每一个队列,如果有时候我们并不像把这些消息给每一个队列,只想给某一些特定的队列,此时我们可以使用Direct定向交换机,通过特定的规则来让其发送给制定队列

这里简单介绍一下,所谓的"规则"就是指定一个"bindingKey",只有bindingKey对上了才会往该队列发消息,比如说调用者像给bindingKey为yellow的队列发消息,那么只有队列的Key为yellow才可以接收到

我们同样通过一个简单实践来学习一下:

这里注意:添加绑定规则是不要忘记添加Key,这里叫routingkey

配置完成后,我们编写方法给定向交换机发送消息,这里我们第二个参数就是填的key,我们先填red,预期结果是交换机会发送消息给两条队列,队列中的消费者都能收到消息

key为red的两条队列都收到了消息,并且被相关消费者所监听

我们换用其他key效果也是一样的,只有相应key的队列可以收到对应的消息

Topic交换机

其实和我们刚刚讲的定向交换机差不多,都是通过指定的key发送给相应的队列,只不过Topic交换机对于key的机制比较灵活,有点像"目录"的形式,key通过通配符的形式让其检测非常灵活(规则如下图所示)

举个简单的例子,假设队列1指定的key是china.#,那么我们调用者发送的消息他所带的key前面只要有china,不管后面是什么比如china.new、china.weather,那么队列1都会接收到

我们用一个简单的小实例来实践一下:

这里绑定key的时候不要忘记格式

编写方法发送信息

可以看到,两条队列的消费者都监听到了消息,者是因为带的key都符合key的检测

这里只有消费者2监听到了

代码自动生成交换机、队列

之前我们的所有例子都是手动在RabbitMQ的控制台自己新增各式各样的交换机以及相应的队列和他们的绑定关系,我们学习阶段这么做可以,但是如果在真实的开发生产环境下这么做风险就很高了,工作交接容易出现问题,失误率比较高,因此我们还是推荐使用代码自动化生成交换机、队列以及他们之间的绑定关系

使用代码生成的方式有两种,一是通过bean对象,第二种是使用注解生成,第一种比较繁琐因此在这里就不作展示介绍了,大家感兴趣自己去搜索,我们主要介绍使用注解的方式来生成

通过注解生成交换机、队列

我们通过下面一个简单的小实例来学习

我们先把之前的相关交换机和队列删除

然后我们通过注解的方式来生成

我们直接在消费者里面做注解声明:

直接在@RabbitListener把里面的属性改为bindings,让值为@QueueBindings

value:填入队列有关信息,name为队列名称,durable为是否开启"持久化"(默认开启)

exchange:填入交换机有关信息,name为交换机名称,type为交换机类型

key:只需要填入相关字符串即可,就是我们前文所讲的bindingKey

我们启动项目,再去RabbitMQ控制台看一下

发现交换机和队列都成功生成了

消息转换器

我们之前发消息的方式一直是调用了rabbitTemplate.convertAndSend方法,这个方法如果不做任何优化会有以下问题:

若传输的消息类型为其他类型比如对象类型,转换时占用的字节会非常多,并且不好读,甚至还有安全风险

因此我们需要对此进行优化

优化很简单,使用JSON序列化代替默认的JDK序列化即可

相关推荐
小陈不好吃1 小时前
Java 泛型与类型擦除:从 PECS 原则到 TypeToken 绕过
java·开发语言·windows·pecs 原则·typetoken绕过
鶴哥只手遮天1 小时前
Spring Framework 7.0.9 深度剖析-03-IoC 容器核心:BeanFactory 与 BeanDefinition 体系
java·后端·开源
ydjs678899-bot1 小时前
如何在Java中打印pdf静默打印和打印对话框选择
java·python·pdf
Shaoshing1 小时前
存储引擎与架构
java·开发语言·架构
鶴哥只手遮天2 小时前
Spring Framework 7.0.9 深度剖析-01-整体架构与设计哲学
java·后端·开源
const_qiu2 小时前
第 2.5 章 2.5.6:前端登录、Token 无感续签与路由守卫
java·前端·人工智能·状态模式
devilnumber2 小时前
Java 代理模式(简单理解回顾)
java·开发语言·代理模式
她的男孩2 小时前
Spring Boot 4 升级别只看编译绿:Forge Admin 在 Jackson、Tomcat JSON 限流、MP 包名与 Flowable 库上踩过的坑
java·spring boot·后端
Knight_AL2 小时前
别把 ThreadLocal 写进 Jackson Serializer
java·spring boot·spring