nestjs使用rabbitMQ

基础知识

rabbitMQ的exchange、bindingkey、routingkey的关系:

Java面试常见问题:RabbitMQ基本概念与工作原理 - 知乎

nestjs的mq使用引导(最后没按照官网的那种方式使用,而是直接编程的方式,这里看下了解下):

RabbitMQ | NestJS 中文文档 | NestJS 中文网

其中,使用的mqlib的API(非常重要,查看了官方的API才能更精准的使用!):

amqplib | Channel API reference

软件

nestjs 8.*.*

使用rabbitMQ需要增加依赖(为什么?看官网:):

"amqp-connection-manager": "^4.1.4",
"amqplib": "^0.10.2",

怎么安装? 不想全局就在当前项目的根目录下执行:

npm install --save amqp-connection-manager amqplib

然后,开发,上代码

代码

mt.tuil.ts

import {Injectable, Logger, OnModuleInit} from '@nestjs/common';
import amqp from 'amqplib';
import * as acm from 'amqp-connection-manager';
import {MqMsgHandler} from './MQMsgHandler';
import {GLoggerService} from '../../framework/logs/glogger';
import ChannelWrapper from "amqp-connection-manager/dist/esm/ChannelWrapper";

const logger = new GLoggerService('RabbitMQ');

// type OnEventHandler = (data: any) => Promise<boolean>;

// @Injectable()
export class RabbitMQClient {
    private connection: acm.AmqpConnectionManager;
    private channelWrapper: acm.ChannelWrapper;
    private rabbitMqConfig: {};
    private url: string;
    private exchange: string;// = 'exchange_event_consume';
    private bindKey: string;
    private virtualHost: string;
    private host: string;
    private port: number;
    private username: string;
    private password: string;
    private msgHandler: MqMsgHandler | undefined;// 是否添加监听者,用于消费使用
    private queue: string;

    constructor(rabbitMqConfig, msgHandler?: MqMsgHandler) {
        this.rabbitMqConfig = rabbitMqConfig;
        this.virtualHost = rabbitMqConfig.virtualHost;
        this.exchange = rabbitMqConfig.exchange;
        this.bindKey = rabbitMqConfig.bindKey;
        this.host = rabbitMqConfig.host;
        this.port = rabbitMqConfig.port;
        this.username = rabbitMqConfig.username;
        this.password = rabbitMqConfig.password;
        this.msgHandler = msgHandler;
        this.queue = rabbitMqConfig.queue;
        this.url = 'amqp://' + rabbitMqConfig.username + ':' + rabbitMqConfig.password + '@'
            + rabbitMqConfig.host + ':' + rabbitMqConfig.port + '/%2F' + rabbitMqConfig.virtualHost;
    }

    public async connect():Promise<void> {
        return new Promise<void>((resolve, reject) => {
            this.connection = acm.connect(this.url, {
                heartbeatIntervalInSeconds: 3,
                reconnectTimeInSeconds: 3,
                connectionOptions: {
                    rejectUnauthorized: false
                },

            });

            this.connection.addListener('connect', () => {
                logger.debug('[客户端]连接成功');
                return resolve();
            });
            this.connection.addListener('connectFailed', (err) => {
                logger.error('[客户端]连接失败!', err.toString());
                return resolve();
            });
            this.connection.addListener('disconnect', (err) => {
                logger.log('[客户端]断开连接%O');
                return reject();
            });
        });
    }

    /**
     * 初始化通道
     */
    async initChannel():Promise<ChannelWrapper> {
        const bindKey = this.bindKey;
        const exchange = this.exchange;
        let channelWrapper;
        const queueName = this.queue;
        if (this.msgHandler) {
            const msgHandler = this.msgHandler;
            logger.log('初始化通道[exchange=' + exchange + ', bindKey=' + bindKey + ', queue=' + queueName + '],并增加通道监听器...');
            channelWrapper = this.connection.createChannel({
                json: false,
                setup: async (channel: amqp.Channel): Promise<any> => {
                    // 初始化事件Exchange
                    await channel.assertExchange(exchange, 'direct', {durable: true});
                    // 初始化事件队列
                    await channel.assertQueue(queueName, {
                        durable: false,
                        autoDelete: true
                    });
                    // 绑定事件队列
                    await channel.bindQueue(queueName, exchange, bindKey);

                    // 设置预消费
                    await channel.prefetch(1);
                    // 消费事件
                    // tslint:disable-next-line:only-arrow-functions
                    channel.consume(queueName, async function (msg: amqp.ConsumeMessage | null): Promise<void> {
                        let data: any;
                        try {
                            if (msg !== undefined && msg !== null && msg.content !== undefined && msg.content !== null) {
                                data = msg.content.toString('utf8') as any;
                                data = JSON.parse(data);
                                if (msgHandler !== undefined && msgHandler !== null) {
                                    await msgHandler(data);
                                } else {
                                    logger.debug(data)
                                }
                                channel.ack(msg);// 应答
                            } else {
                                logger.debug('事件数据异常 ' + data);
                                return Promise.reject(`事件数据异常`);
                            }
                        } catch (error) {
                            logger.debug('[consume]' + error);
                            channel.ack(msg);
                        }


                    }, {noAck: false});// 需要应答
                }
            });
            logger.log('完成!');
        } else {
            logger.log('初始化通道[exchange=' + exchange + ', bindKey=' + bindKey + ', queue=' + queueName + ']');
            channelWrapper = this.connection.createChannel({
                json: false
            });
            logger.log('完成!');
        }

        channelWrapper.on('connect', () => {
            logger.log('[通道]连接成功');
        });
        channelWrapper.on('close', () => {
            logger.log('[通道]连接关闭');
        });
        channelWrapper.on('error', (err) => {
            logger.error('[通道]连接错误: ', err.toString());
        });
        this.channelWrapper = channelWrapper;
        return channelWrapper;
    }

    // tslint:disable-next-line:no-unused-expression
    async publish(data: any | {}): Promise<boolean> {
        if (!this.channelWrapper) {
            logger.error('通道没有初始化!', '');
            return Promise.reject('通道没有初始化!');
        }
        const BIND_KEY = this.bindKey;
        const exchange = this.exchange;
        return this.channelWrapper
            .publish(exchange, BIND_KEY, Buffer.from(JSON.stringify(data)))
            // .sendToQueue(queue, Buffer.from(JSON.stringify(data)))
            .then(() => {
                logger.log('publish Message was sent!')
                return true;
            })
            .catch((err) => {
                logger.error('publish Message was rejected...  Boo!', err.toString());
                return false;
            });
    }


    // tslint:disable-next-line:no-unused-expression
    public async start() {
        await this.connect();
        await this.initChannel();
    }
}

说明:

1. 我封装的这个工具用来创建一个客户端出来,通过publish来发布数据,通过创建之初执行初始化方法start()来初始化连接(没交给nest的inject是因为我需要具备自己的特殊控制)。

2. 创建时构造函数传入参数,第一个参数是初始化参数,结构见rabbitconfig.ts.第二个参数是订阅的回调函数,用于处理收听到的消息,如果不传就不会创建监听。这个回调函数定义了一个类型,见mqMsgHandler.ts.

mqmsghandler.ts

export type MqMsgHandler = (data: any) => Promise<boolean>;

说明:消息的回调函数

rabbitconfig.ts

export class RabbitConfig{
    private _host:string;
    private _port:number;
    private _queue:string;
    private _bindKey:string;
    private _exchange:string;
    private _username:string;
    private _password:string;
    private _virtualHost:string;


    constructor(host: string, port: number, queue: string, bindKey: string, exchange: string, username: string, password: string, virtualHost: string) {
        this._host = host;
        this._port = port;
        this._queue = queue;
        this._bindKey = bindKey;
        this._exchange = exchange;
        this._username = username;
        this._password = password;
        this._virtualHost = virtualHost;
    }

    get host(): string {
        return this._host;
    }

    set host(value: string) {
        this._host = value;
    }

    get port(): number {
        return this._port;
    }

    set port(value: number) {
        this._port = value;
    }

    get queue(): string {
        return this._queue;
    }

    set queue(value: string) {
        this._queue = value;
    }

    get bindKey(): string {
        return this._bindKey;
    }

    set bindKey(value: string) {
        this._bindKey = value;
    }

    get exchange(): string {
        return this._exchange;
    }

    set exchange(value: string) {
        this._exchange = value;
    }

    get username(): string {
        return this._username;
    }

    set username(value: string) {
        this._username = value;
    }

    get password(): string {
        return this._password;
    }

    set password(value: string) {
        this._password = value;
    }

    get virtualHost(): string {
        return this._virtualHost;
    }

    set virtualHost(value: string) {
        this._virtualHost = value;
    }
}

net_disk.service.ts

注意:代码经过一些删除处理,可能直接粘贴无法运行,但是核心逻辑都在里面。

import {GLoggerService} from '../framework/logs/glogger';
import {RabbitMQClient} from '../util/mq/mq.util';
import {Injectable} from '@nestjs/common';
import * as config from 'config';
import {RabbitConfig} from '../util/mq/RabbitConfig';
import {MqMsgHandler} from '../util/mq/MQMsgHandler';
import {data} from 'cheerio/lib/api/attributes';

const rabbitMQ = config['rabbitMQ'];

@Injectable()
export class NetDiskService {
    private readonly glogger = new GLoggerService();
    private mQClientSubscriber: RabbitMQClient;//订阅者
    private mQClientSender: RabbitMQClient;//发布者

    /**
     * 由于定于与发布不是同一个queue,所以需要建立两个client
     */
    constructor() {
        const subscribe = new RabbitConfig(
            rabbitMQ.host,
            rabbitMQ.port,
            rabbitMQ.subscribe.queue,
            rabbitMQ.subscribe.bindKey,
            rabbitMQ.exchange,
            rabbitMQ.username,
            rabbitMQ.password,
            rabbitMQ.virtualHost
        );
        this.mQClientSubscriber = new RabbitMQClient(subscribe,
            // tslint:disable-next-line:no-empty
            async (msg: any) => {
                console.log('收到0发送的消息:' + JSON.stringify(msg));
                return await this.onNetDiskUploadGeomFile(msg);
            }
        );

        const send = new RabbitConfig(
            rabbitMQ.host,
            rabbitMQ.port,
            rabbitMQ.send.queue,
            rabbitMQ.send.bindKey,
            rabbitMQ.exchange,
            rabbitMQ.username,
            rabbitMQ.password,
            rabbitMQ.virtualHost
        );
        this.mQClientSender = new RabbitMQClient(send,async (msg: any) => {
            console.log('收到1发送的消息:' + JSON.stringify(msg));
            return Promise.resolve(true);
        });

        this.mQClientSubscriber.start();
        this.mQClientSender.start();
    }

    
    public async publishMTo1(metaData: any): Promise<number> {
        try {
            console.log("发送消息给1")
            let result = await this.mQClientSender.publish(metaData);
            if (!result) {
                return 1
            }
            return 0
        } catch (e) {
            this.glogger.error('失败!', e);
        }

        return 1
    }


    
    public async publishTo0(metaData: any): Promise<number> {
        try {
            console.log("发送消息给0")
            let result = await this.mQClientSubscriber.publish(metaData);
            if (!result) {
                return 1
            }
            return 0
        } catch (e) {
            this.glogger.error('失败!', e);
        }

        return 1
    }
}

完成

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NestJS 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架。它提供了一种简洁的方式来创建模块化、可测试和可维护的应用程序。 RabbitMQ 是一个开源的消息队列中间件,它实现了高级消息队列协议(AMQP),提供了可靠的消息传递机制,支持多种消息模式,如点对点、发布/订阅等。 在 NestJS使用 RabbitMQ 可以通过一些库和插件来实现。下面是一种常见的使用方式: 1. 安装依赖: 首先,需要安装 `amqplib` 库,它是 RabbitMQ 的官方 Node.js 客户端库。可以使用 npm 进行安装: ``` npm install amqplib ``` 2. 创建 RabbitMQ 连接: 在 NestJS 中,可以在应用程序的根模块中创建 RabbitMQ 连接。可以使用 `amqplib` 提供的 `connect` 方法来创建连接,并将其注入到应用程序的提供者中。 3. 发布消息: 在需要发布消息的地方,可以通过调用 RabbitMQ 连接的 `createChannel` 方法来创建一个通道。然后,可以使用通道的 `publish` 方法来发布消息到指定的交换机和队列。 4. 订阅消息: 在需要订阅消息的地方,可以通过调用 RabbitMQ 连接的 `createChannel` 方法来创建一个通道。然后,可以使用通道的 `consume` 方法来监听指定队列的消息,并在接收到消息时执行相应的处理逻辑。 这只是一种简单的使用 RabbitMQ 的方式,实际上还可以根据具体需求进行更复杂的配置和使用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值