RabbitMQ: 高级特性实践之生产者确认与消费者容器实现

生产者端:消息确认(Publisher Confirms)与返回机制(Return Messages)


1 ) 技术背景:

RabbitMQ 通过 Publisher Confirms 确保消息到达 Broker,通过 Return Messages 处理无法路由的消息。在 NestJS 中需显式开启这两项特性,并解决异步回调中的消息追踪问题。

2 ) 关键配置步骤

  1. 启用 ConnectionFactory 特性

    // rabbitmq.config.ts
    import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
    import { ConfirmChannel } from 'amqplib';
    
    @Module({
      imports: [
        RabbitMQModule.forRoot(RabbitMQModule, {
          exchanges: [{ name: 'order_exchange', type: 'direct' }],
          uri: 'amqp://localhost:5672',
          connectionInitOptions: { wait: true },
          // 开启生产者确认与返回模式
          connectionManagerOptions: {
            connectionOptions: {
              confirm: true, // 等效于 setPublisherConfirmType(ConfirmType.CORRELATED)
              clientProperties: { publisherReturns: true } // 开启消息返回
            }
          }
        })
      ]
    })
    export class RabbitConfigModule {}
    
  2. 配置 RabbitTemplate 回调

    // rabbit.service.ts
    import { RabbitRPC, RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
    import { RabbitMQProducer } from '@golevelup/nestjs-rabbitmq';
    
    @Injectable()
    export class RabbitService {
      constructor(private readonly producer: RabbitMQProducer) {}
    
      async sendOrderMessage(orderId: string) {
        const correlationId = orderId.toString();
        await this.producer.publish('order_exchange', 'order.routing.key', 
          { id: orderId },
          { 
            correlationId,
            mandatory: true, // 触发 ReturnCallback
            headers: { 'x-delay': 5000 }
          }
        );
    
        // 注册全局回调(需在模块初始化时完成)
        this.producer.registerConfirmCallback((ack: boolean, correlation: any) => {
          if (ack) {
            console.log(`[Confirm] Message ${correlation.id} confirmed`);
          } else {
            console.error(`[Confirm] Message ${correlation.id} nacked`);
          }
        });
    
        this.producer.registerReturnCallback((returned) => {
          console.error(
            `[Return] Message ${returned.properties.correlationId} returned: ` +
            `Code=${returned.replyCode}, Reason=${returned.replyText}`
          );
        });
      }
    }
    
  3. 异步回调关联消息
    使用 correlationId 绑定消息与业务标识(如订单ID),解决异步确认中的消息追踪问题:

    // 消息发送时携带 correlationId
    await this.rabbitService.sendOrderMessage('order_123');
    

消费者端:SimpleMessageListenerContainer 高级特性


容器核心能力:

  • 多队列监听
  • 并发消费者线程池管理
  • 手动消息确认(Manual Ack)
  • 消费端限流(QoS Prefetch)

消费者容器配置

// order.consumer.ts
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
 
@Controller()
export class OrderConsumer {
  @RabbitSubscribe({
    exchange: 'order_exchange',
    routingKey: 'order.routing.key',
    queue: 'order_queue',
    queueOptions: { durable: true },
    // 关键参数配置
    allowNonJsonMessages: true,
    prefetchCount: 1, // 消费端限流 (QoS)
    ackMode: 'MANUAL' // 手动确认模式
  })
  async handleOrderMessage(
    message: {}, 
    @Ctx() context: AmqpConnection
  ) {
    const { channel, deliveryTag } = context.getChannelRef();
    try {
      console.log(`Processing message: ${JSON.stringify(message)}`);
      // 业务逻辑处理...
      channel.ack(deliveryTag, false); // 手动确认
    } catch (error) {
      channel.nack(deliveryTag, false, true); // 重回队列
    }
  }
}

高级特性实现

  1. 手动消息确认
    通过 ackMode: 'MANUAL'channel.ack() 控制消息确认时机,异常时调用 nack() 实现重回队列。

  2. 消费端限流
    prefetchCount: 1 限制每次仅预取一条消息,避免消费者过载。

  3. 动态线程池调整
    @golevelup/nestjs-rabbitmq 中通过装饰器参数控制并发:

    @RabbitSubscribe({
      prefetchCount: 5, // 每个信道预取量
      concurrency: 3    // 并发消费者数量
    })
    

工程示例:NestJS 集成 RabbitMQ 的三种方案


1 ) 方案1:基础生产者-消费者模型

// 生产者
@Injectable()
export class ProducerService {
  constructor(private amqpConnection: AmqpConnection) {}
 
  async publishEvent(exchange: string, routingKey: string, payload: object) {
    await this.amqpConnection.publish(exchange, routingKey, payload);
  }
}
 
// 消费者
@RabbitSubscribe({ exchange: 'events', routingKey: '#' })
handleEvent(msg: {}) {
  console.log(`Received: ${JSON.stringify(msg)}`);
}

2 ) 方案2:RPC 模式(请求-响应)

// 服务端
@RabbitRPC({
  exchange: 'rpc_exchange',
  routingKey: 'math.sqrt',
  queue: 'sqrt_queue'
})
async sqrtRPC(num: number) {
  return { result: Math.sqrt(num) };
}
 
// 客户端调用
const response = await this.amqpConnection.request<{ result: number }>({
  exchange: 'rpc_exchange',
  routingKey: 'math.sqrt',
  payload: 9
});

3 ) 方案3:死信队列(DLX)实现延迟消息

// 配置死信交换器
@Module({
  imports: [
    RabbitMQModule.forRootAsync(RabbitMQModule, {
      useFactory: () => ({
        exchanges: [
          { 
            name: 'dlx_exchange', 
            type: 'direct',
            options: { durable: true }
          }
        ],
        queues: [
          {
            name: 'delayed_queue',
            options: {
              deadLetterExchange: 'dlx_exchange',
              messageTtl: 10000 // 10秒后转入死信队列
            }
          }
        ]
      })
    })
  ]
})

RabbitMQ 运维命令参考


# 查看队列状态
rabbitmqctl list_queues name messages_ready messages_unacknowledged
 
# 监控信道预取设置
rabbitmqctl list_channels name prefetch_count
 
# 创建死信队列
rabbitmqadmin declare queue name=dlx_queue durable=true
rabbitmqadmin declare exchange name=dlx_exchange type=direct
rabbitmqadmin declare binding source=dlx_exchange destination=dlx_queue routing_key=dead

关键总结:

  1. 生产者确认需同时开启 confirmpublisherReturns 参数,通过 correlationId 关联异步回调
  2. 消费者容器通过 @RabbitSubscribe 实现线程池/限流/手动ACK的集中管理
  3. 工程方案选择取决于场景:基础消息(方案1)、同步调用(方案2)、延迟任务(方案3)
  4. 运维核心:prefetch_count 控制消费速率,dead-letter-exchange 处理异常消息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值