原文地址:www.cnblogs.com/makalochen/… 根据上文实践
一、docker安装
拉取镜像,需要带management 这个包含客户端 方便配置
docker pull rabbitmq:3-management
启动容器
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin123 --restart=always rabbitmq:3-management
注意:
--name rabbitmq:给容器命名为 rabbitmq。
-p 5672:5672:映射 RabbitMQ 客户端通信端口(程序连接用)。
-p 15672:15672:映射 Web 管理界面端口(浏览器访问用)。
RABBITMQ_DEFAULT_USER 和 RABBITMQ_DEFAULT_PASS:设置默认管理员账号密码(根据需求修改)。
--restart=always:确保 Docker 重启时容器自动启动。
访问:http://localhost:15672 使用启动时的账号密码登录
二、nestjs 使用
1.下载完整依赖
npm i --save amqplib amqp-connection-manager @nestjs/microservices @golevelup/nestjs-rabbitmq
2.可以先安装日志模块
npm install --save nest-winston winston winston-daily-rotate-file
配置:
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MqPublistModule } from './mq-publist/mq-publist.module';
// 日志模块
import { WinstonModule } from 'nest-winston';
import { MqSubscribeModule } from './mq-subscribe/mq-subscribe.module';
import * as winston from 'winston';
import 'winston-daily-rotate-file';
@Module({
imports: [
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(),
),
}),
],
}),
MqSubscribeModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
3.连接RabbitMQ,这块建一个MqPublistModule,里面的连接地址自行修改
import { Module } from '@nestjs/common';
import {
RabbitMQModule,
MessageHandlerErrorBehavior,
} from '@golevelup/nestjs-rabbitmq';
import { MqPublistService } from './mq-publist.service';
@Module({
imports: [
RabbitMQModule.forRootAsync({
useFactory: () => {
return {
// 交换机配置
exchanges: [
{
// 交换机名称
name: `exchanges_test`,
/**
* 交换机类型
* direct: 直连交换机,根据消息的路由键(routing key)将消息发送到一个或多个绑定的队列。
fanout: 扇形交换机,将消息广播到所有绑定的队列,无需指定路由键。
topic: 主题交换机,根据消息的路由键模式匹配将消息发送到一个或多个绑定的队列。
headers: 头交换机,根据消息的头部信息将消息发送到一个或多个绑定的队列。
*/
type: 'direct',
// 其他选项
// 持久化(Durable): 指定交换机、队列或消息是否需要在服务器重启后保留
options: { durable: false },
},
],
// 连接的url
uri: 'amqp://admin:admin123@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 {}
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');
});
}
}
// exchange
private readonly exc_test = `exchanges_test`;
// routingKey
private readonly routingKey_test = 'key_task';
/**
* 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,
)}`,
);
}
}
启动项目后 可以看到
4.在RabbitMQ客户端配置交换机,注意 启动后 自动创建交换机
5.接下来创建队列 ,我这边创建一个发布任务 一个返回结果的队列
6.将队列与对应的key进行绑定,进入交换机,绑定好后,自动在3处显示
7.添加消息发送的逻辑,随便某个接口调用就行
8.添加订阅模块,创建MqSubscribeModule
import { Module } from '@nestjs/common';
import { MqSubscribeService } from './mq-subscribe.service';
@Module({
providers: [MqSubscribeService]
})
export class MqSubscribeModule {}
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: ['key_result'],
// 队列
queue: `inspection_result_queue`,
// 持久化配置
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);
}
}
注意,我这边起了两个服务,一个发任务,接收结果,另一个接收任务,发送结果,自行修改对应的key和queue就行,最终测试结果如下: