NestJs 使用 RabbitMQ

NestJs 使用 RabbitMQ

既然是使用 RabbitMQ 那先不管其他的 把 RabbitMQ 装上再说

RabbitMQ 安装

这里直接找他们官网就行

Installing RabbitMQ | RabbitMQ

这里我们选择使用 docker 安装 快捷方便

这里直接参考:

https://juejin.cn/post/7198430801850105916

我们要站在巨人的肩膀上,快速学习,具体命令

RabbitMQ docker方式安装

# 下载最新的代码 management 的镜像
docker pull rabbitmq:management
# 创建数据卷
docker volume create rabbitmq-home
# 启动容器
docker run -id --name=rabbitmq -v rabbitmq-home:/var/lib/rabbitmq -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin rabbitmq:management

这里除了挂载数据卷之外,还暴露了两个端口,以及设定了两个环境变量:

  • 15672端口:RabbitMQ的管理页面端口
  • 5672端口:RabbitMQ的消息接收端口
  • RABBITMQ_DEFAULT_USER环境变量:指定RabbitMQ的用户名,这里我指定为admin,大家部署时替换成自己定义的
  • RABBITMQ_DEFAULT_PASS环境变量:指定RabbitMQ的密码,这里我指定为admin,大家部署时替换成自己定义的

这样容器就部署完成了!在浏览器访问你的服务器地址:15672即可访问到RabbitMQ的管理界面,用户名和密码即为刚刚指定的环境变量的配置值。

这里没有指定LANG=C.UTF-8,是因为RabbitMQ容器默认就是这个语言环境,无需我们再设定。

image-20230506164331483

访问管理页面

http://localhost:15672/
用户名:admin
密码:admin

image-20230506164730603

可以看到已经进去了

前置知识

RabbitMQ的exchange、bindingkey、routingkey的关系

理解 RabbitMQ Exchange - 知乎 原文

https://www.cnblogs.com/makalochen/p/17378002.html 转载

总之:

从 AMQP 协议可以看出,Queue、Exchange 和 Binding 构成了 AMQP 协议的核心

  • Producer:消息生产者,即投递消息的程序。

  • Broker:消息队列服务器实体。

    • Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
    • Binding:绑定,它的作用就是把 Exchange 和 Queue 按照路由规则绑定起来。
    • Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
  • Consumer:消息消费者,即接受消息的程序。

Binding 表示 Exchange 与 Queue 之间的关系,

我们也可以简单的认为队列对该交换机上的消息感兴趣,

绑定可以附带一个额外的参数 RoutingKey。

Exchange 就是根据这个 RoutingKey 和当前 Exchange 所有绑定的 Binding 做匹配,

如果满足匹配,就往 Exchange 所绑定的 Queue 发送消息,

这样就解决了我们向 RabbitMQ 发送一次消息,可以分发到不同的 Queue。

RoutingKey 的意义依赖于交换机的类型。

amqb api 文档

amqplib | Channel API reference

只有看了官方文档才能更正确的使用

NesJs 使用 mq 文档

https://docs.nestjs.cn/9/microservices?id=rabbitmq

日志依赖

https://www.npmjs.com/package/winston

GitHub - winstonjs/winston: A logger for just about everything.

GitHub - gremo/nest-winston: A Nest module wrapper form winston logger

https://docs.nestjs.cn/9/techniques?id=日志

https://juejin.cn/post/7187910528918880311

npm install --save nest-winston winston winston-daily-rotate-file

NestJs 中使用

安装依赖包

npm i --save amqplib amqp-connection-manager @nestjs/microservices

上面三个包基础包,这里还有方便的包

nestjs/packages/rabbitmq/README.md at master · golevelup/nestjs · GitHub

所以完整的安装依赖应该为

npm i --save amqplib amqp-connection-manager @nestjs/microservices @golevelup/nestjs-rabbitmq

创建 发布消息模块

nest g mo mqPublist
nest g s mqPublist

image-20230506175359587

这样使用cli 工具就自动给我们 将 service 和 module 关联起来了,并在 全局模块中注册了

连接RabbitMQ

在写其他代码之前我们首先要保证,连接正常

全局注册模块

首先保证我们的 MqPublistModule模块在全局注册

app.module.ts

import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsController } from './cats/cats.controller';
import { MakaloModule } from './makalo/makalo.module';
import { UploadModule } from './upload/upload.module';
import { UserModule } from './user/user.module';
import { Module1Module } from './module1/module1.module';
import { ConfigModule } from './config/config.module';
import { PModule } from './p/p.module';import { MqPublistModule } from './mq-publist/mq-publist.module';
// 日志模块
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import 'winston-daily-rotate-file';
@Module({imports: [MakaloModule, UploadModule, UserModule, Module1Module,ConfigModule.forRoot({ path: '/makalo' }),PModule,MqPublistModule,// 日志模块WinstonModule.forRoot({transports: [new winston.transports.DailyRotateFile({dirname: `logs`, // 日志保存的目录filename: '%DATE%.log', // 日志名称,占位符 %DATE% 取值为 datePattern 值。datePattern: 'YYYY-MM-DD', // 日志轮换的频率,此处表示每天。zippedArchive: true, // 是否通过压缩的方式归档被轮换的日志文件。maxSize: '20m', // 设置日志文件的最大大小,m 表示 mb 。maxFiles: '14d', // 保留日志文件的最大天数,此处表示自动删除超过 14 天的日志文件。// 记录时添加时间戳信息format: winston.format.combine(winston.format.timestamp({format: 'YYYY-MM-DD HH:mm:ss',}),winston.format.json(),),}),],}),],controllers: [AppController, CatsController],providers: [AppService],
})
export class AppModule { }

image-20230508154800610

MqPublistModule 模块的RabbitMQ 配置
import { Module } from '@nestjs/common';
import { RabbitMQModule, MessageHandlerErrorBehavior } from '@golevelup/nestjs-rabbitmq';
import { MqPublistService } from './mq-publist.service';@Module({imports: [RabbitMQModule.forRootAsync(RabbitMQModule, {useFactory: () => {return {// 交换机配置exchanges: [{// 交换机名称name: `exchanges_test`,/*** 交换机类型* direct: 直连交换机,根据消息的路由键(routing key)将消息发送到一个或多个绑定的队列。fanout: 扇形交换机,将消息广播到所有绑定的队列,无需指定路由键。topic: 主题交换机,根据消息的路由键模式匹配将消息发送到一个或多个绑定的队列。headers: 头交换机,根据消息的头部信息将消息发送到一个或多个绑定的队列。*/type: 'direct',// 其他选项// 持久化(Durable): 指定交换机、队列或消息是否需要在服务器重启后保留options: { durable: false },},],// 连接的urluri: 'amqp://admin:admin@localhost:5672',/*** 用于配置 RabbitMQ 连接的选项。它是一个对象,可以包含以下属性:wait: 一个布尔值,表示是否等待连接成功后才开始启动应用程序。默认为 true。rejectUnauthorized: 一个布尔值,表示是否拒绝不受信任的 SSL 证书。默认为 true。timeout: 一个数字,表示连接超时时间(以毫秒为单位)。默认为 10000 毫秒。heartbeatIntervalInSeconds: 一个数字,表示心跳间隔时间(以秒为单位)。默认为 60 秒。channelMax: 一个数字,表示最大通道数。默认为 65535。这些选项将影响 RabbitMQ 连接的行为和性能。您可以根据需要进行调整*/connectionInitOptions: { wait: false },/*** 用于启用直接回复模式。当设置为 true 时,* 生产者将使用 replyTo 和 correlationId 字段指定的队列和标识符来接收响应,* 而不是使用默认生成的匿名队列。这使得消费者可以将响应直接发送到请求者所在的队列,* 从而避免了性能上的开销和消息传递中断的问题。* * 这里设置为false*/enableDirectReplyTo: false,// 通道的默认预取计数。prefetchCount: 300,/**用于配置 RabbitMQ 消费者订阅的默认错误处理行为选项。当消费者处理消息时出现错误时,可以使用该选项来指定消费者应如何处理这些错误。MessageHandlerErrorBehavior.ACK 表示在发生错误时自动确认消息并从队列中删除以避免消息反复传递和死信队列的问题。如果您想要更多的控制权来处理错误,可以将其设置为 MessageHandlerErrorBehavior.NACK,然后手动决定是否重新排队或丢弃该消息。*/defaultSubscribeErrorBehavior: MessageHandlerErrorBehavior.ACK,};},}),],providers: [MqPublistService],exports: [MqPublistService],
})
export class MqPublistModule {}

image-20230508143349824

MqPublistService 中的 基本设置
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';@Injectable()
export class MqPublistService implements OnModuleInit {constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,private readonly amqp: AmqpConnection) {}/*** onModuleInit 是 NestJS 中一个生命周期钩子方法,* 它是 @nestjs/common 模块提供的 OnModuleInit 接口的一部分。* 实现了该接口并实现了 onModuleInit 方法的类,在模块加载时会自动执行该方法*/async onModuleInit() {// 启动监听this.monitorConn();}/*** rabbitmq连接监听*/monitorConn() {const conn = this.amqp.managedConnection;if (conn) {conn.on('connect', () => {this.logger.info('rabbitmq broker connect');});conn.on('disconnect', () => {this.logger.error('rabbitmq broker disconnect');});}const chan = this.amqp.managedChannel;if (chan) {chan.on('connect', () => {this.logger.info('rabbitmq channel connect');});chan.on('error', () => {this.logger.error('rabbitmq channel error');});chan.on('close', () => {this.logger.error('rabbitmq channel close');});}}
}

image-20230508143536357

启动
npm run start:dev

image-20230508154847518

这时候我们查看 RabbitMQ 的管理面板,会发现我们配置的交换机出现了

image-20230508155039170

NestJs RabbitMQ 发送队列消息_案例

如果你看过前置知识,你就知道最重要的三个东西

exchange、routingkey, Queue

上面在NestJs 中已经配置了默认的 交换姬

img

但是 routingkey, Queue 他们之间的绑定关系还没得呢,这时候我们手动设置一下

打开RabbitMQ 的 管理页面

http://localhost:15672/

设置 routingkey, Queue 绑定关系

找到这个交换机,点进去

image-20230508170832877

设置 队列名 和 Routing Key 点击绑定

image-20230508171157557

image-20230508171232762

这时候 我们就将 exchange、routingkey, Queue 关联起来了

全局模块注册

app.module.ts

import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsController } from './cats/cats.controller';
import { MakaloModule } from './makalo/makalo.module';
import { UploadModule } from './upload/upload.module';
import { UserModule } from './user/user.module';
import { Module1Module } from './module1/module1.module';
import { ConfigModule } from './config/config.module';
import { PModule } from './p/p.module';import { MqPublistModule } from './mq-publist/mq-publist.module';
// 日志模块
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import 'winston-daily-rotate-file';
@Module({imports: [MakaloModule, UploadModule, UserModule, Module1Module,ConfigModule.forRoot({ path: '/makalo' }),PModule,MqPublistModule,// 日志模块WinstonModule.forRoot({transports: [new winston.transports.DailyRotateFile({dirname: `logs`, // 日志保存的目录filename: '%DATE%.log', // 日志名称,占位符 %DATE% 取值为 datePattern 值。datePattern: 'YYYY-MM-DD', // 日志轮换的频率,此处表示每天。zippedArchive: true, // 是否通过压缩的方式归档被轮换的日志文件。maxSize: '20m', // 设置日志文件的最大大小,m 表示 mb 。maxFiles: '14d', // 保留日志文件的最大天数,此处表示自动删除超过 14 天的日志文件。// 记录时添加时间戳信息format: winston.format.combine(winston.format.timestamp({format: 'YYYY-MM-DD HH:mm:ss',}),winston.format.json(),),}),],}),],controllers: [AppController, CatsController],providers: [AppService],
})
export class AppModule { }

image-20230508172830549

MqPublistModule 模块配置

mq-publist.module.ts

import { Module } from '@nestjs/common';
import { RabbitMQModule, MessageHandlerErrorBehavior } from '@golevelup/nestjs-rabbitmq';
import { MqPublistService } from './mq-publist.service';@Module({imports: [RabbitMQModule.forRootAsync(RabbitMQModule, {useFactory: () => {return {// 交换机配置exchanges: [{// 交换机名称name: `exchanges_test`,/*** 交换机类型* direct: 直连交换机,根据消息的路由键(routing key)将消息发送到一个或多个绑定的队列。fanout: 扇形交换机,将消息广播到所有绑定的队列,无需指定路由键。topic: 主题交换机,根据消息的路由键模式匹配将消息发送到一个或多个绑定的队列。headers: 头交换机,根据消息的头部信息将消息发送到一个或多个绑定的队列。*/type: 'direct',// 其他选项// 持久化(Durable): 指定交换机、队列或消息是否需要在服务器重启后保留options: { durable: false },},],// 连接的urluri: 'amqp://admin:admin@localhost:5672',/*** 用于配置 RabbitMQ 连接的选项。它是一个对象,可以包含以下属性:wait: 一个布尔值,表示是否等待连接成功后才开始启动应用程序。默认为 true。rejectUnauthorized: 一个布尔值,表示是否拒绝不受信任的 SSL 证书。默认为 true。timeout: 一个数字,表示连接超时时间(以毫秒为单位)。默认为 10000 毫秒。heartbeatIntervalInSeconds: 一个数字,表示心跳间隔时间(以秒为单位)。默认为 60 秒。channelMax: 一个数字,表示最大通道数。默认为 65535。这些选项将影响 RabbitMQ 连接的行为和性能。您可以根据需要进行调整*/connectionInitOptions: { wait: false },/*** 用于启用直接回复模式。当设置为 true 时,* 生产者将使用 replyTo 和 correlationId 字段指定的队列和标识符来接收响应,* 而不是使用默认生成的匿名队列。这使得消费者可以将响应直接发送到请求者所在的队列,* 从而避免了性能上的开销和消息传递中断的问题。* * 这里设置为false*/enableDirectReplyTo: false,// 通道的默认预取计数。prefetchCount: 300,/**用于配置 RabbitMQ 消费者订阅的默认错误处理行为选项。当消费者处理消息时出现错误时,可以使用该选项来指定消费者应如何处理这些错误。MessageHandlerErrorBehavior.ACK 表示在发生错误时自动确认消息并从队列中删除以避免消息反复传递和死信队列的问题。如果您想要更多的控制权来处理错误,可以将其设置为 MessageHandlerErrorBehavior.NACK,然后手动决定是否重新排队或丢弃该消息。*/defaultSubscribeErrorBehavior: MessageHandlerErrorBehavior.ACK,};},}),],providers: [MqPublistService],exports: [MqPublistService],
})
export class MqPublistModule {}

image-20230508173027708

MqPublistService 封装

mq-publist.service.ts

import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { WINSTON_MODULE_NEST_PROVIDER, WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';@Injectable()
export class MqPublistService implements OnModuleInit {constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,private readonly amqp: AmqpConnection) {}/*** onModuleInit 是 NestJS 中一个生命周期钩子方法,* 它是 @nestjs/common 模块提供的 OnModuleInit 接口的一部分。* 实现了该接口并实现了 onModuleInit 方法的类,在模块加载时会自动执行该方法*/async onModuleInit() {// 启动监听this.monitorConn();}/*** rabbitmq连接监听*/monitorConn() {const conn = this.amqp.managedConnection;if (conn) {conn.on('connect', () => {this.logger.info('rabbitmq broker connect');});conn.on('disconnect', () => {this.logger.error('rabbitmq broker disconnect');});}const chan = this.amqp.managedChannel;if (chan) {chan.on('connect', () => {this.logger.info('rabbitmq channel connect');});chan.on('error', () => {this.logger.error('rabbitmq channel error');});chan.on('close', () => {this.logger.error('rabbitmq channel close');});}}// exchangeprivate readonly exc_test = `exchanges_test`;// routingKeyprivate readonly routingKey_test = 'routingKey_test';/*** rabbitmq发送消息* @param message*/async pubMQMsgTest(message: any): Promise<void> {await this.amqp.publish(this.exc_test, this.routingKey_test, message);this.logger.info(`amqp publish message -> exchange : ${this.exc_test}, routingKey : ${this.routingKey_test},message : ${JSON.stringify(message,)}`,);}
}

image-20230508173153066

其他模块中使用

import { MqPublistService } from '../mq-publist/mq-publist.service';constructor(private readonly mqPublishService: MqPublistService,) { }// 发送 RabbitMQ 消息
this.mqPublishService.pubMQMsgTest('test send push RabbitMQ');

image-20230508173505452

RabbitMQ 管理页面中查看

image-20230508173558842

单击队列名直接跳转到对应的队列

image-20230508173634690

image-20230508173756729

NestJs RabbitMQ 订阅队列消息_案例

nest g mo mqSubscribe
nest g s mqSubscribe

image-20230508175258258

image-20230508185755082

MqSubscribeModule

mq-subscribe.module.ts

import { Module } from '@nestjs/common';
import { MqSubscribeService } from './mq-subscribe.service';@Module({providers: [MqSubscribeService]
})
export class MqSubscribeModule {}

image-20230508185850275

MqSubscribeService

mq-subscribe.service.ts

import { Inject, Injectable } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
import { Logger } from 'winston';@Injectable()
export class MqSubscribeService {constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger,) { }@RabbitSubscribe({// 交换机exchange: `exchanges_test`,routingKey: ['routingKey_test',],// 队列queue: `queue_test`,// 持久化配置queueOptions: { durable: true },})// 收到队列的订阅消息自动调用该方法async subscribe(data: any): Promise<any> {const routingKey = arguments[1].fields.routingKey;console.log('arguments[1].fields.exchange :', arguments[1].fields.exchange);console.log('routingKey :', routingKey);console.log('data:', data);this.logger.info(`amqp receive msg,exchange is ${arguments[1].fields.exchange},routingKey is ${routingKey},msg is ${JSON.stringify(data,)}`,);}
}

image-20230508185924832

使用上面的发送消息再次访问

http://localhost:3000/p

image-20230508190030734

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

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

相关文章

鸿蒙面试心得

自疫情过后&#xff0c;java和web前端都进入了冰河时代。年龄、薪资、学历都成了找工作路上躲不开的门槛。 年龄太大pass 薪资要高了pass 学历大专pass 好多好多pass 找工作的路上明明阳关普照&#xff0c;却有一种凄凄惨惨戚戚说不清道不明的“优雅”意境。 如何破局&am…

宿主机无法通过ip连接wsl2解决方案

文章目录 原因排查网络模式win11防火墙关闭wsl ubuntu防火墙 如果之前能连接现在连接不上可以参考该方案 原因排查 网络模式win11防火墙(win11新增了Hyper-V防火墙)wsl2 ubuntu防火墙 网络模式 wsl2的默认网络模式是NAT&#xff0c;建议修改为镜像模式。在C:\Users\<User…

【深度学习】【Lora训练3】StabelDiffusion,Lora训练过程,秋叶包,Linux,SDXL Lora训练

为了便于使用&#xff0c;构建一个docker镜像来使用秋叶包。2024年6月26日。 docker run -it --gpus all -v /ssd/xiedong:/datax --net host kevinchina/deeplearning:pytorch2.3.0-cuda12.1-cudnn8-devel-xformers bashgit clone --recurse-submodules https://github.com/A…

408计算机网络--物理层

一、物理层概述 物理层是干嘛使得&#xff1f; 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体。 物理层主要任务是确定与传输媒体接口有关的一些特性。定义标准可以理解为插排上的两孔三孔 机械特性&#xff1a;定义物理连接…

Rill Data:实时数据分析的未来

欢迎来到 Rill Rill是从数据湖到仪表板的最快路径。 rilldata 与大多数 BI 工具不同&#xff0c;Rill 带有自己的嵌入式内存数据库。数据和计算位于同一位置&#xff0c;查询以毫秒为单位返回。 因此&#xff0c;您可以即时透视、切片和深入研究数据。 下载 Rill 开始建模数…

标签接口开发(富含完整CRUD开发流程)

文章目录 1.easyCode生成CRUD1.生成代码2.查看代码3.调整代码1.SubjectLabelDao.xml发现生成的select语句不带逗号&#xff01;&#xff01;&#xff01;1.解决方法&#xff1a;2.entity.java.vm3.dao.java.vm4.Mapper.xml.vm 2.重新生成代码3.SubjectLabelDao.java 删除Pageab…

【RedHat】使用VMware Workstation创建配置RedHat操作系统

目录 &#x1f31e;1.前言 &#x1f31e;2. 使用 VMware Workstation 创建配置RedHat &#x1f33c;2.1 VMware Workstation 创建虚拟机 &#x1f33c;2.2 安装RedHat 7.6 &#x1f30a;2.2.1 添加光盘 &#x1f30a;2.2.2 开始安装操作系统 &#x1f30a;2.2.3 系统初始…

从基础到前沿:PLM产品生命周期管理系统在物料管理中的应用

在当今竞争激烈的市场中&#xff0c;制造型企业必须不断寻求提高效率和降低成本的方法。物料管理作为企业内部物流的核心环节&#xff0c;对于控制成本、提高生产效率、加快产品上市时间具有至关重要的作用。本文将探讨物料管理的重要性&#xff0c;以及如何通过三品产品生命周…

Python数据可视化-地图可视化

1.首先绘制实现数据可视化的思维导图 具体要实现什么功能-怎么处理&#xff0c;先把思路写好 数据来源&#xff1a; 爬取的数据 运行结果&#xff1a; 部分代码&#xff1a; 完整代码请在下方↓↓↓&#x1f447;获取 转载请注明出处&#xff01;

75101A 1553B总线测试模块

75101A 1553B总线测试模块 75101A 1553B总线测试模块是单通道多功能&#xff0c;符合CPCI/PXI总线的标准3U尺寸模块&#xff0c;可同时用作BC、RTs和BM&#xff0c;其中BM具有比特误码、highbit、lowbit、highword、lowword、校验错误、消息错误检测以及最大256M字节的数据捕…

新能源革命风起云涌:创新科技引领可持续发展新篇章

随着全球气候变化和环境问题日益严峻&#xff0c;新能源革命正以其不可阻挡的势头&#xff0c;席卷着世界的每一个角落。 创新科技在这场革命中发挥着至关重要的作用&#xff0c;它不仅是新能源开发利用的引擎&#xff0c;更是推动可持续发展的关键力量。 新能源革命的核心在于…

ubuntu如何切换到root用户

1、主要指令&#xff1a; sudo -i su root 2、示例 3、其他说明 在Ubuntu&#xff08;以及大多数其他基于Linux的操作系统中&#xff09;&#xff0c;切换到root用户通常意味着获得了对系统的完全访问权限。这种权限允许执行以下操作&#xff08;但不限于这些&#xff09;…

Spring AI 实现调用openAi 多模态大模型

什么是多模态? 多模态(Multimodal)指的是数据或信息的多种表现形式。在人工智能领域,我们经常会听到这个词,尤其是在近期大型模型(如GPT-4)开始支持多模态之后。 模态:模态是指数据的一种形式,例如文本、图像、音频等。每一种形式都是一种模态。多模态:多模态就是将…

ctfshow 新春欢乐赛 web

web1 <?phphighlight_file(__FILE__); error_reporting(0);$content $_GET[content]; file_put_contents($content,<?php exit();.$content);?contentphp://filter/string.rot13|<?cuc flfgrz(yf /);?>|/resourceshell.php绕过死亡exit 但是我发现个问题就是…

Fragment与ViewModel(MVVM架构)

简介 在Android应用开发中&#xff0c;Fragment和ViewModel是两个非常重要的概念&#xff0c;它们分别属于架构组件库的一部分&#xff0c;旨在帮助开发者构建更加模块化、健壮且易维护的应用。 Fragment Fragment是Android系统提供的一种可重用的UI组件&#xff0c;它能够作为…

基于51单片机密码锁—六位密码LCD1602显示

基于51单片机密码锁设计 &#xff08;仿真&#xff0b;程序&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.密码为六位数字&#xff0c;有键按下&#xff0c;LCD1602就会显示出字符 “*”&#xff1b; 2.当用户键入正确密码后&#xff0c;再按确认键&a…

是时候系统性的的学习那些开发中必备的软件了!

大家好&#xff0c;我是 王有志&#xff0c;一个分享硬核 Java 技术的金融摸鱼侠&#xff0c;欢迎大家加入 Java 人自己的交流群“共同富裕的 Java 人”。 最近小组内心来了两个实习生&#xff0c;各方面都很不错&#xff0c;基础知识扎实&#xff0c;勤奋好学&#xff08;卷&…

记一次 APK 逆向动静调试 + so 动态链接库分析

0x00 前言&#xff1a; 好久没有做过安卓逆向了&#xff0c;最近重新系统地学习了安卓逆向技术。找到了一道较为典型的逆向分析题来练手&#xff0c;以锻炼动静态分析和动态链接库分析的基本能力。在这里记录基本的分析流程手法。 0x01 逆向分析&#xff1a; 一、使用 Genym…

IPython最简洁方便的Python语法测试工具

前言和需求 相信不少人使用JS时&#xff0c;最常用的快捷键是F12。网页调试&#xff0c;不仅可以调试我们的前端页面&#xff0c;对于多数后端JS程序员来讲&#xff0c;我们有个啥语法不确定了&#xff0c;直接开个网页F12确认一下就行了。甚至写了个方法&#xff0c;不确定对…

springboot + Vue前后端项目(第十九记)

项目实战第十九记 写在前面1. redis安装(windows安装)1.1 获取软件链接地址&#xff1a;1.2 启动redis1.3 测试是否启动成功1.4 通过 Another Redis DeskTop软件可视化查看redis 2. SpringBoot集成redis2.1 引入依赖2.2 注入RedisTemplate2.3 使用redis2.4 redis更新2.5 redis使…