RabbitMQ: 订单→餐厅→骑手→结算的消息驱动架构设计

Spring Boot 整合 RabbitMQ 实战:异步解耦 + 削峰填谷,让系统更抗造(企业级消息队列落地) SpringBoot整合RabbitMQ实现异步处理与系统解耦,解决图书管理系统中的性能瓶颈问题。通过消息队列实现图书借阅短信通知、批量导入处理和高并发削峰等功能。文章详细介绍了RabbitMQ核心概念(生产者、消费者、队列、交换机等)及其在企业应用中的三大价值:异步解耦、削峰填谷和可靠投递。提供了完整的代码实现方案,包括基础消息发送/接收、延迟队列(用于图书超期提醒)和企业级批量导入场景。同时总结了6个常见问题及解决方案,如消息持久化、手动确认、并发数设置等。该方案能显著提升系统响应速度,将同步处理3秒的 阅读详情

微服务订单系统架构


1 ) 核心业务流程

用户下单 → 订单服务存库 → 通知餐厅 → 餐厅确认 → 分配骑手 → 结算服务 → 更新订单状态

2 ) 核心业务流程设计

在微服务架构中,订单创建流程需完成两个核心操作:

  1. 订单数据持久化:将用户请求数据存储至数据库
  2. 异步消息通知:通过消息队列通知下游服务(如餐厅服务)

3 ) 订单创建流程遵循严格的事务边界控制:

  • 数据库操作必须保证原子性
  • 消息发送需确保至少一次送达
  • 服务解耦通过消息队列实现服务间通信

4 ) 完整调用流程

用户订单服务数据库RabbitMQ商家服务骑手服务POST /orders1保存订单(status=CREATING)2操作确认3发布商家订单消息4投递订单消息5返回ACK确认6传递商家响应7更新订单(status=PENDING)8操作确认9发布骑手调度消息10投递调度消息11用户订单服务数据库RabbitMQ商家服务骑手服务

订单服务实现详解


1 ) 实体对象映射体系

// 订单创建视图对象(前端交互)
class OrderCreateVO {
  address: string;
  accountId: number;
  productId: number;
}
 
// 订单持久化实体(数据库映射)
@Entity()
class OrderDetailPO {
  @PrimaryGeneratedColumn()
  id: number;
 
  @Column()
  address: string;
 
  @Column()
  accountId: number;
 
  @Column()
  productId: number;
 
  @Column({
    type: 'enum',
    enum: OrderStatus,
    default: OrderStatus.CREATING
  })
  status: OrderStatus;
 
  @CreateDateColumn()
  createdAt: Date;
}
 
// 消息传输对象(服务间通信)
class OrderMessageDTO {
  orderId: number;
  productId: number;
  accountId: number;
}

2 )核心业务逻辑实现

// order.service.ts 
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as amqp from 'amqplib';
 
@Injectable()
export class OrderService {
  constructor(
    @InjectRepository(OrderDetailPO)
    private orderRepository: Repository<OrderDetailPO>
  ) {}
 
  async createOrder(createVO: OrderCreateVO): Promise<void> {
    // 1. 构建持久化对象 
    const orderPO = new OrderDetailPO();
    orderPO.address = createVO.address;
    orderPO.accountId = createVO.accountId;
    orderPO.productId = createVO.productId;
    orderPO.status = OrderStatus.CREATING;
 
    // 2. 数据库持久化(TypeORM)
    const savedOrder = await this.orderRepository.save(orderPO);
 
    // 3. 构建消息对象
    const messageDTO = new OrderMessageDTO();
    messageDTO.orderId = savedOrder.id; // 必须使用数据库生成ID
    messageDTO.productId = savedOrder.productId;
    messageDTO.accountId = savedOrder.accountId;
 
    // 4. 发送RabbitMQ消息
    await this.sendRestaurantMessage(messageDTO);
  }
 
  private async sendRestaurantMessage(dto: OrderMessageDTO): Promise<void> {
    const connection = await amqp.connect('amqp://localhost');
    const channel = await connection.createChannel();
    
    // 使用JSON序列化
    const message = JSON.stringify(dto);
    const exchangeName = 'exchange_order_restaurant';
    const routingKey = 'restaurant.order';
 
    // 发布消息(注意字节转换)
    await channel.assertExchange(exchangeName, 'direct', { durable: true });
    channel.publish(
      exchangeName,
      routingKey,
      Buffer.from(message),
      { persistent: true } // 消息持久化 
    );
 
    await channel.close();
    await connection.close();
  }
}

关键技术说明:

  1. ID生成策略:
    必须使用数据库返回的orderId(通过@PrimaryGeneratedColumn()实现),不可使用前端传入ID
  2. 消息序列化:
    采用JSON格式进行对象序列化(JSON.stringify()),确保跨语言兼容性
  3. 消息持久化:
    设置persistent: true保证Broker重启后消息不丢失
  4. 路由键约定:
    使用预定义的routingKey实现消息精准路由

3 )控制器层实现

// order.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { OrderService } from './order.service';
 
@Controller('orders')
export class OrderController {
  constructor(private readonly orderService: OrderService) {}
 
  @Post()
  async createOrder(@Body() createVO: OrderCreateVO): Promise<void> {
    // 结构化日志记录 
    this.logger.log(`Creating order for account ${createVO.accountId}`, {
      productId: createVO.productId,
      address: createVO.address
    });
 
    // 调用服务层
    await this.orderService.createOrder(createVO);
  }
}

4 )日志记录最佳实践:

// 正确示例(避免字符串拼接)
logger.log('Creating order for account {}', createVO.accountId);
 
// 错误示例(产生无效拼接)
logger.log('Creating order for account ' + createVO.accountId);

优势:

  • 延迟字符串拼接(仅在实际输出时执行)
  • 减少不必要的内存分配
  • 支持日志级别过滤(DEBUG日志在生产环境不执行拼接)

消息驱动架构:订单状态更新与微服务协同


核心流程设计(4个关键阶段)

商家确认订单
本地事务更新
发件箱写入事件
消息中继投递
骑手微服务消费

消息处理机制与订单状态更新流程,当商家微服务完成业务处理后,会通过 RabbitMQ 向订单微服务回复消息,订单微服务收到消息后需执行以下核心操作:

1 ) 消息接收与反序列化

通过 RabbitMQ 的 DeliverCallback 接口处理消息:

// 创建消息消费者
channel.consume('ORDER_QUEUE', (msg) => {
  if (msg) {
    try {
      // 消息体转换 
      const messageBody = msg.content.toString();
      // 反序列化DTO 
      const orderMessageDTO = JSON.parse(messageBody) as OrderMessageDTO;
      
      // 订单状态处理逻辑
      await updateOrderStatus(orderMessageDTO);
      
      // 手动确认消息 
      channel.ack(msg);
    } catch (error) {
      // 错误日志记录 
      this.logger.error(`消息处理失败: ${error.message}`, error.stack);
      channel.nack(msg); // 消息重回队列
    }
  }
});

2 )数据库操作与状态判断

从数据库获取订单实体并判断当前状态:

// 获取订单实体 
const order = await this.orderRepository.findOne({
  where: { id: orderMessageDTO.orderId }
});

if (!order) {
  throw new Error(`订单不存在: ${orderMessageDTO.orderId}`);
}

// 状态机判断 
switch (order.status) {
  case OrderStatus.CREATING:
    // 商家确认处理
    break;
  case OrderStatus.RESTAURANT_CONFIRMED:
    // 骑手服务处理
    break;
  default:
    this.logger.warn(`无效订单状态: ${order.status}`);
}

3 )商家服务确认逻辑实现

当订单处于 CREATING 状态时,说明收到的是商家微服务回复:

if (order.status === OrderStatus.CREATING) {
  // 验证商家确认数据 
  if (
    orderMessageDTO.isConfirmed && 
    orderMessageDTO.price !== null && 
    orderMessageDTO.price !== undefined
  ) {
    // 更新订单状态和价格
    order.status = OrderStatus.RESTAURANT_CONFIRMED;
    order.price = orderMessageDTO.price;
    await this.orderRepository.save(order);
 
    // 触发下一阶段:通知骑手服务
    await this.notifyDeliveryService(order);
  } else {
    // 商家确认失败处理 
    order.status = OrderStatus.FAILED;
    await this.orderRepository.save(order);
    this.logger.error(`商家确认失败: 订单#${order.id}`);
  }
}

4 )骑手服务通知机制

订单状态更新为 RESTAURANT_CONFIRMED 后,需通知骑手微服务:

private async notifyDeliveryService(order: Order) {
  // 构造消息体 
  const deliveryMessage = {
    orderId: order.id,
    address: order.deliveryAddress,
    estimatedTime: 30 // 预估配送时间(分钟)
  };
 
  // 发送到骑手服务交换机 
  await this.rabbitClient.publish(
    'DELIVERY_EXCHANGE',
    'delivery.new',
    Buffer.from(JSON.stringify(deliveryMessage)),
    { persistent: true } // 消息持久化
  );
  
  this.logger.log(`已通知骑手服务: 订单#${order.id}`);
}

异步消息处理机制设计与实现——消费者监听、线程池优化


1 )核心问题与解决思路

在微服务架构中,订单服务需监听商户服务的消息响应,并向骑手服务转发消息。关键问题在于:

  1. 消费者监听机制缺失
    需实现消息接收后自动触发业务逻辑,但未注册队列监听器。
  2. 线程资源管理风险
    消费者线程需长期运行,若直接阻塞主线程或滥用异步线程,将导致线程爆炸。

解决方案:

  • 使用 basicConsume 注册队列监听器,绑定 DeliverCallback 处理消息。
  • 通过 线程池 管理异步任务,结合 @Async 注解实现非阻塞调用。

2 )RabbitMQ 消费者监听实现

技术要点:

  1. basicConsume 方法参数解析:

    • queue:监听的队列名(如 order_queue)。
    • autoAck:是否自动确认消息(设为 true 时,消息被消费后立即从队列删除)。
    • callback:消息处理回调接口(即 DeliverCallback)。
    • consumerTag:消费者标签(可留空)。
  2. 消费者线程保活:
    注册监听后,线程需通过 sleep 长期运行,否则监听器失效。

// NestJS 等效实现(使用 amqplib 库)
import { connect, Channel, Message } from 'amqplib';
import { Injectable } from '@nestjs/common';
 
@Injectable()
export class OrderMessageService {
  private channel: Channel;
 
  async handleMessage(): Promise<void> {
    const connection = await connect('amqp://localhost');
    this.channel = await connection.createChannel();
 
    // 声明队列与绑定(伪代码)
    await this.channel.assertQueue('order_queue');
    // ...其他队列绑定逻辑 
 
    // 注册消费者监听
    this.channel.consume(
      'order_queue',
      (msg: Message | null) => {
        if (msg) {
          this.processMessage(msg.content.toString());
          this.channel.ack(msg); // 手动确认消息(若 autoAck=false)
        }
      },
      { noAck: true } // 等同于 autoAck=true
    );
 
    // 线程保活(实际通过 NestJS 生命周期管理)
    while (true) {
      await new Promise(resolve => setTimeout(resolve, 24 * 60 * 60 * 1000)); // 模拟长时阻塞
    }
  }
 
  private processMessage(content: string): void {
    // 业务逻辑:向骑手服务发送消息
    console.log(`Received message: ${content}`);
  }
}

3 ) 线程池优化与异步任务管理
关键风险:直接启动异步线程可能导致线程资源耗尽。
NestJS 解决方案:

  1. 配置专用线程池:
    // src/task/task.config.ts
    import { Injectable } from '@nestjs/common';
    import { AsyncOptions, AsyncConfiguration } from '@nestjs/async';
    
    @Injectable()
    export class TaskConfig implements AsyncConfiguration {
      createAsyncOptions(): AsyncOptions {
        return {
          useFactory: () => ({
            corePoolSize: 10,      // 核心线程数
            maxPoolSize: 50,       // 最大线程数
            queueCapacity: 100,    // 缓冲队列长度 
            keepAliveSeconds: 60,  // 空闲线程存活时间
            threadNamePrefix: 'rabbit_async_', // 线程名前缀
            awaitTermination: true, // 关闭时等待任务完成
            awaitTerminationSeconds: 30, // 等待超时时间
          }),
        };
      }
    }
    
  2. 集成异步装饰器:
    // src/order/order.message.service.ts
    import { Async } from '@nestjs/async';
    
    export class OrderMessageService {
      @Async({ pool: 'rabbit_pool' }) // 指定线程池
      async handleMessage(): Promise<void> { ... }
    }
    

4 )服务启动与监听器初始化
通过 NestJS 生命周期钩子 触发监听:

// src/rabbit/rabbit.config.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { OrderMessageService } from '../order/order.message.service';
 
@Injectable()
export class RabbitConfig implements OnModuleInit {
  constructor(private readonly orderMessageService: OrderMessageService) {}
 
  async onModuleInit(): Promise<void> {
    await this.orderMessageService.handleMessage(); // 应用启动时自动执行 
  }
}

5 )Controller 层完善

补充 HTTP 接口映射与请求体解析:

// src/order/order.controller.ts 
import { Body, Controller, Post } from '@nestjs/common';
import { OrderService } from './order.service';
 
@Controller('orders')
export class OrderController {
  constructor(private readonly orderService: OrderService) {}
 
  @Post()
  async createOrder(@Body() orderData: any): Promise<void> {
    await this.orderService.processOrder(orderData); // 调用业务逻辑
  }
}

商家微服务开发全流程


1 )核心开发步骤与技术要点

  1. 工程初始化与配置

    • 新建restaurant-service微服务工程,配置数据库连接确保服务可启动
    • 设置服务端口8081避免与订单微服务端口冲突
  2. 枚举定义

    // enums/product-status.enum.ts
    export enum ProductStatus {
      AVAILABLE = 'AVAILABLE',    // 商品可售卖
      NOT_AVAILABLE = 'NOT_AVAILABLE' // 商品不可售卖
    }
    
    // enums/restaurant-status.enum.ts
    export enum RestaurantStatus {
      OPEN = 'OPEN',   // 商户营业中
      CLOSED = 'CLOSED' // 商户已关闭
    }
    
    // enums/order-status.enum.ts(与订单微服务保持一致)
    export enum OrderStatus {
      CREATED = 'CREATED',
      CONFIRMED = 'CONFIRMED',
      CANCELLED = 'CANCELLED'
    }
    
  3. 数据传输对象(DTO)定义

    // dtos/order-message.dto.ts
    export class OrderMessageDTO {
      orderId: string;
      productId: string;
      restaurantId: string;
      confirmedPrice?: number; // 确认后的价格
      isConfirmed: boolean;    // 订单是否确认有效 
    }
    
  4. 持久化对象(PO/Entity)定义

    // entities/product.entity.ts
    import { ProductStatus } from '../enums/product-status.enum';
    
    @Entity('product')
    export class Product {
      @PrimaryGeneratedColumn('uuid')
      id: string;
      
      @Column()
      name: string;
      
      @Column('decimal')
      price: number;
      
      @Column()
      restaurantId: string;
      
      @Column({ type: 'enum', enum: ProductStatus })
      status: ProductStatus;
      
      @CreateDateColumn()
      createdAt: Date;
    }
    
    // entities/restaurant.entity.ts
    import { RestaurantStatus } from '../enums/restaurant-status.enum';
    
    @Entity('restaurant')
    export class Restaurant {
      @PrimaryGeneratedColumn('uuid')
      id: string;
      
      @Column()
      name: string;
      
      @Column()
      address: string;
      
      @Column({ type: 'enum', enum: RestaurantStatus })
      status: RestaurantStatus;
      
      @CreateDateColumn()
      createdAt: Date;
    }
    
  5. 数据访问层(DAO/Repository)

    // repositories/product.repository.ts
    import { EntityRepository, Repository } from 'typeorm';
    
    @EntityRepository(Product)
    export class ProductRepository extends Repository<Product> {
      async findById(id: string): Promise<Product | undefined> {
        return this.createQueryBuilder('product')
          .where('product.id = :id', { id })
          .getOne();
      }
    }
    
    // repositories/restaurant.repository.ts
    @EntityRepository(Restaurant)
    export class RestaurantRepository extends Repository<Restaurant> {
      async findById(id: string): Promise<Restaurant | undefined> {
        return this.createQueryBuilder('restaurant')
          .where('restaurant.id = :id', { id })
          .getOne();
      }
    }
    

2 )消息处理业务逻辑实现

核心流程:

  1. 接收订单微服务的RabbitMQ消息
  2. 校验商品和商户状态
  3. 计算并填充商品价格
  4. 返回订单确认结果
// services/order-handler.service.ts
import { Injectable } from '@nestjs/common';
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
import { ProductRepository } from '../repositories/product.repository';
import { RestaurantRepository } from '../repositories/restaurant.repository';
 
@Injectable()
export class OrderHandlerService {
  constructor(
    private readonly productRepo: ProductRepository,
    private readonly restaurantRepo: RestaurantRepository
  ) {}
 
  @RabbitSubscribe({
    exchange: 'order_exchange',
    routingKey: 'order.restaurant',
    queue: 'restaurant_queue'
  })
  async handleOrderMessage(message: OrderMessageDTO) {
    // 1. 查询商品信息
    const product = await this.productRepo.findById(message.productId);
    
    // 2. 查询商户信息
    const restaurant = await this.restaurantRepo.findById(message.restaurantId);
 
    // 3. 状态校验与价格填充
    if (product && restaurant && 
        product.status === ProductStatus.AVAILABLE &&
        restaurant.status === RestaurantStatus.OPEN) {
      message.confirmedPrice = product.price;
      message.isConfirmed = true;
    } else {
      message.isConfirmed = false;
    }
 
    // 4. 返回处理结果(发送到响应队列)
    return message;
  }
}

3 )RabbitMQ 配置详解

// rabbitmq/rabbitmq.module.ts
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
 
@Module({
  imports: [
    RabbitMQModule.forRoot(RabbitMQModule, {
      exchanges: [
        {
          name: 'order_exchange',
          type: 'direct',
          options: { durable: true } // 持久化交换机
        }
      ],
      uri: 'amqp://localhost:5672', // RabbitMQ连接地址
      connectionInitOptions: { wait: true }
    }),
  ],
  exports: [RabbitMQModule]
})
export class RabbitMQConfigModule {}
 
// 队列绑定配置(在服务初始化时执行)
async function setupRabbitMQBindings(channel: Channel) {
  await channel.assertExchange('order_exchange', 'direct', { durable: true });
  
  // 声明并绑定商户队列
  await channel.assertQueue('restaurant_queue', { 
    durable: true, 
    exclusive: false 
  });
  await channel.bindQueue(
    'restaurant_queue',
    'order_exchange',
    'order.restaurant' // 路由键
  );
}

基于RabbitMQ Fanout交换机的订单结算系统实现


核心流程:订单服务 → 结算服务的异步结算广播

1 ) Fanout交换机特性解析

  1. 路由机制

    • 忽略路由键(Routing Key):当消息发送至FanoutExchange时,无论RoutingKey为何值,均被完全忽略
    • 广播投递:消息会复制多份并投递到所有绑定队列(如绑定3个队列则生成3条消息)
    • 典型场景:适用于事件广播(如订单结算通知需同步多个子系统)
  2. 技术优势

    • 解耦性强:生产者无需感知消费者数量与身份
    • 扩展便捷:新增消费者只需绑定队列到交换机

2 ) 业务流程重构

1. 更新订单状态
2. 分配骑手
3. 发送结算消息
4. 银行结算
5. 返回结果
订单服务
骑手服务
结算服务
银行系统
  1. 关键状态迁移

    • 订单状态流:CREATING → RESTAURANT_CONFIRMED → DELIVERYMAN_CONFIRMED(当前节点)
    • 结算触发条件:当状态为DELIVERYMAN_CONFIRMED且骑手ID非空时发起结算
  2. 异常处理

    • 骑手分配失败:订单状态置为 FAILED 并终止流程
    • 消息重试机制:通过deliveryTag实现消息确认(ACK/NACK)

3 ) 实现RabbitMQ结算服务

  1. 环境配置(.env)

    RABBITMQ_HOST=localhost
    RABBITMQ_PORT=5672
    SETTLEMENT_QUEUE=settlement_queue
    FANOUT_EXCHANGE=settlement_fanout 
    
  2. 核心模块实现

  • RabbitMQ连接模块(rabbitmq.module.ts)

    import { Module } from '@nestjs/common';
    import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
     
    @Module({
      imports: [
        RabbitMQModule.forRoot(RabbitMQModule, {
          exchanges: [
            {
              name: 'settlement_fanout',
              type: 'fanout',
              options: { durable: true } // 持久化交换机
            }
          ],
          uri: `amqp://${process.env.RABBITMQ_HOST}:${process.env.RABBITMQ_PORT}`,
        }),
      ],
      exports: [RabbitMQModule],
    })
    export class RabbitMqModule {}
    
  • 结算服务业务逻辑(settlement.service.ts)

    import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import { SettlementEntity } from './settlement.entity';
     
    interface OrderMessageDTO {
      orderId: string;
      accountId: string;
      price: number;
    }
     
    @Injectable()
    export class SettlementService {
      constructor(
        @InjectRepository(SettlementEntity)
        private settlementRepo: Repository<SettlementEntity>,
      ) {}
     
      async processSettlement(order: OrderMessageDTO) {
        // 模拟银行结算(实际需接入支付宝/微信SDK)
        const transactionId = this.generateTransactionId(); 
     
        // 持久化结算记录
        const settlement = this.settlementRepo.create({
          orderId: order.orderId,
          accountId: order.accountId,
          amount: order.price,
          status: 'SUCCESS',
          transactionId,
          date: new Date(),
        });
        await this.settlementRepo.save(settlement);
        return settlement;
      }
     
      private generateTransactionId(): string {
        return `TX${Date.now()}${Math.floor(Math.random() * 10000)}`;
      }
    }
    
  • 消息消费者(settlement.consumer.ts)

    import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
    import { Injectable } from '@nestjs/common';
    import { SettlementService } from './settlement.service';
     
    @Injectable()
    export class SettlementConsumer {
      constructor(private settlementService: SettlementService) {}
     
      @RabbitSubscribe({
        exchange: 'settlement_fanout',
        routingKey: '', // Fanout交换机忽略routingKey
        queue: process.env.SETTLEMENT_QUEUE,
        queueOptions: { durable: true },
      })
      async handleSettlementMessage(message: any) {
        try {
          const orderData: OrderMessageDTO = JSON.parse(message);
          await this.settlementService.processSettlement(orderData);
          // 实际需向订单服务发送结算结果(通过Direct Exchange)
        } catch (error) {
          console.error('结算失败', error);
          // 重试或补偿机制
        }
      }
    }
    
  • 生产者(订单服务侧 - order.service.ts)

    import { RabbitMQService } from '@golevelup/nestjs-rabbitmq';
     
    @Injectable()
    export class OrderService {
      constructor(private readonly rabbitMqService: RabbitMQService) {}
     
      async notifySettlement(orderId: string) {
        const orderData = await this.getOrderDetails(orderId); // 获取订单数据
        this.rabbitMqService.publish('settlement_fanout', '', {
          orderId: orderData.id,
          accountId: orderData.userId,
          price: orderData.totalPrice,
        });
      }
    }
    

4 ) RabbitMQ 运维命令

声明Fanout交换机 
rabbitmqadmin declare exchange name=settlement_fanout type=fanout durable=true
 
绑定队列到交换机(忽略routingKey)
rabbitmqadmin declare queue name=settlement_queue durable=true
rabbitmqadmin declare binding source=settlement_fanout destination=settlement_queue

5 ) 关键设计要点

  1. 消息可靠性

    • 生产者确认:启用publisher confirms机制
    • 消费者ACK:手动确认消息处理完成
    @RabbitSubscribe({ 
      /* ... */,
      allowNonJsonMessages: true,
      errorHandler: (channel, msg) => channel.nack(msg, false, true) // 重试
    })
    
  2. 数据一致性

    • 事务补偿:结算失败时通过Saga模式回滚订单状态
    • 幂等设计:结算记录使用orderId+transactionId唯一索引
  3. 性能优化

    • 连接复用:使用RabbitMQ连接池避免频繁TCP握手
    • 批量插入:TypeORM的insert批量处理结算记录

术语说明:

  • FanoutExchange:广播型交换机,无视路由键直接复制消息到所有绑定队列
  • RoutingKey:消息路由键(在Direct/Topic模式中决定队列路由)
  • Durable:持久化设置(重启后保留交换机/队列)
  • Saga模式:分布式事务解决方案,通过事件驱动维护跨服务数据一致性

Fanout交换机的正确使用与循环消息问题解决方案


1 )业务场景与问题复现
在四微服务(订单、餐厅、骑手、结算)联调过程中,发现循环接收消息的异常现象:结算服务处理完业务后向Exchange发送消息,却再次被自身消费。根本原因是采用Fanout类型Exchange时,若多个服务共用同一Exchange,所有绑定队列均会收到广播消息。

关键问题诊断:

  1. Fanout特性误解:
    Fanout Exchange会将消息广播到所有绑定队列,不适用于点对点通信。
  2. Exchange命名冲突:
    订单服务与结算服务共用order.settlement Exchange,导致消息循环(订单服务发送 → 结算服务消费并响应 → 响应消息又被结算服务自身消费)

2 )解决方案:双Exchange隔离通信

通过创建两个独立Exchange实现单向通信隔离:

发送至
发送至
订单服务
order.settlement
结算服务队列
结算服务
settlement.order
订单服务队列

修正步骤:

  1. Exchange声明分离:

    • 订单服务声明 order.settlement(供结算服务监听)
    • 结算服务声明 settlement.order(供订单服务监听)
  2. 队列绑定修正:

    # 管控台操作示例 
    # 解绑错误绑定
    rabbitmqadmin unbind queue name=settlement_queue exchange=order.settlement 
    
    # 结算服务队列绑定到订单服务的Exchange
    rabbitmqadmin bind queue name=settlement_queue exchange=order.settlement 
    
    # 订单服务队列绑定到结算服务的Exchange
    rabbitmqadmin bind queue name=order_queue exchange=settlement.order
    

3 ) 基于NestJS的RabbitMQ实现

  1. 依赖与配置
// settlement.module.ts
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { SettlementController } from './settlement.controller';
 
@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'SETTLEMENT_SERVICE',
        transport: Transport.RMQ,
        options: {
          urls: ['amqp://user:password@localhost:5672'],
          queue: 'settlement_queue',
          exchange: 'order.settlement', // 监听订单服务的Exchange 
          exchangeType: 'fanout',
          noAck: false, // 关闭自动ACK 
        },
      },
      {
        name: 'ORDER_CLIENT',
        transport: Transport.RMQ,
        options: {
          urls: ['amqp://user:password@localhost:5672'],
          exchange: 'settlement.order', // 向订单服务发送的Exchange
          exchangeType: 'fanout',
        },
      },
    ]),
  ],
  controllers: [SettlementController],
})
export class SettlementModule {}
  1. 消息处理与业务逻辑
// settlement.controller.ts 
import { Controller } from '@nestjs/common';
import { MessagePattern, Payload } from '@nestjs/microservices';
import { SettlementService } from './settlement.service';
 
@Controller()
export class SettlementController {
  constructor(private readonly settlementService: SettlementService) {}
 
  @MessagePattern('settlement_queue')
  async handleOrderSettlement(@Payload() message: any) {
    try {
      // 1. 解析消息并执行业务 
      const settlementData = JSON.parse(message.content.toString());
      const transactionId = await this.settlementService.processSettlement(settlementData);
 
      // 2. 构造响应消息(向订单服务发送)
      const response = { transactionId, status: 'COMPLETED' };
      this.settlementService.sendToOrderExchange(response);
 
    } catch (error) {
      // 3. 错误处理与消息重试 
      await this.settlementService.handleFailure(message, error);
    }
  }
}
  1. 服务层核心方法
// settlement.service.ts
import { Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
 
@Injectable()
export class SettlementService {
  constructor(
    @Inject('ORDER_CLIENT') private orderClient: ClientProxy,
  ) {}
 
  async processSettlement(data: any): Promise<string> {
    // 1. 数据库操作(TypeORM示例)
    const settlement = new SettlementEntity();
    settlement.orderId = data.orderId;
    settlement.amount = data.amount;
    await settlement.save();
 
    // 2. 返回事务ID
    return settlement.transactionId;
  }
 
  sendToOrderExchange(response: object) {
    // 向订单服务的Exchange发送消息 
    this.orderClient.emit('', Buffer.from(JSON.stringify(response)));
  }
 
  async handleFailure(message: any, error: Error) {
    // 消息重试机制(需安装amqplib)
    const channel = message.channel;
    if (message.fields.deliveryTag < 3) {
      channel.nack(message, false, true); // 重试3次
    } else {
      channel.nack(message, false, false); // 进入死信队列 
      await this.logFailure(error);
    }
  }
}

4 ) 调试关键与经验总结

  1. 管控台验证:

    • 确认Exchange创建:order.settlement 与 settlement.order
    • 检查队列绑定:结算队列绑定order.settlement,订单队列绑定settlement.order
  2. 断点调试技巧:

    • 订单服务:在消息发送前拦截(确认目标Exchange)
    • 结算服务:在消息消费入口拦截(验证消息来源)
  3. Fanout适用场景:

    场景类型适用性案例
    事件广播✅订单状态变更通知多个服务
    服务间点对点通信❌订单→结算的定向请求

工程实例1


1 )依赖配置(package.json )

"dependencies": {
  "@nestjs/common": "^9.0.0",
  "@nestjs/microservices": "^9.0.0",
  "amqplib": "^0.8.0",
  "typeorm": "^0.3.0"
}

2 ) RabbitMQ连接工厂

// rabbitmq.factory.ts  
import * as amqp from 'amqplib';
 
export class RabbitMQFactory {
  static async createConnection(): Promise<amqp.Connection> {
    return amqp.connect('amqp://user:password@localhost:5672'); 
  }
 
  static async createChannel(
    connection: amqp.Connection 
  ): Promise<amqp.Channel> {
    return connection.createChannel(); 
  }
}

3 )消息生产者服务封装

// message-producer.service.ts  
import { Injectable } from '@nestjs/common';
import { RabbitMQFactory } from './rabbitmq.factory'; 
 
@Injectable()
export class MessageProducerService {
  async publish(
    exchange: string,
    routingKey: string,
    message: object
  ): Promise<void> {
    const conn = await RabbitMQFactory.createConnection(); 
    const channel = await RabbitMQFactory.createChannel(conn); 
    
    await channel.assertExchange(exchange,  'direct', { durable: true });
    channel.publish( 
      exchange,
      routingKey,
      Buffer.from(JSON.stringify(message)), 
      { persistent: true }
    );
    
    setTimeout(() => {
      channel.close(); 
      conn.close(); 
    }, 500);
  }
}

4 )消费者服务实现

// restaurant.consumer.ts 
import { Process, Processor } from '@nestjs/bull';
import * as amqp from 'amqplib';
 
@Processor('restaurant_orders')
export class RestaurantConsumer {
  @Process()
  async handleOrderMessage(job: Job) {
    const conn = await amqp.connect('amqp://localhost'); 
    const channel = await conn.createChannel(); 
    
    const queue = 'restaurant_order_queue';
    await channel.assertQueue(queue,  { durable: true });
    
    channel.consume(queue,  (msg) => {
      if (msg !== null) {
        const content = JSON.parse(msg.content.toString()); 
        console.log('Received:',  content);
        
        // 业务处理逻辑
        this.processOrder(content); 
        
        channel.ack(msg); 
      }
    });
  }
 
  private processOrder(order: OrderMessageDTO): void {
    // 实现餐厅接单业务逻辑
  }
}

5 ) 关键配置项

// main.ts  微服务配置 
const app = await NestFactory.createMicroservice(AppModule,  {
  transport: Transport.RMQ,
  options: {
    urls: ['amqp://localhost:5672'],
    queue: 'restaurant_order_queue',
    queueOptions: {
      durable: true,
      deadLetterExchange: 'dead_letter_exchange' // 死信队列配置
    },
    noAck: false // 开启手动确认 
  }
});

工程示例:2


1 )RabbitMQ 模块配置

// rabbitmq.module.ts
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
 
@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'RABBITMQ_SERVICE',
        transport: Transport.RMQ,
        options: {
          urls: [process.env.RABBITMQ_URL],
          queue: 'ORDER_QUEUE',
          queueOptions: {
            durable: true, // 持久化队列
            arguments: { 'x-queue-type': 'quorum' } // 高可用配置 
          },
          noAck: false,    // 手动确认模式
          prefetchCount: 5 // 服务质量控制
        }
      }
    ])
  ],
  exports: [ClientsModule]
})
export class RabbitMQModule {}

2 )订单状态实体定义

// order.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
 
export enum OrderStatus {
  CREATING = 'CREATING',
  RESTAURANT_CONFIRMED = 'RESTAURANT_CONFIRMED',
  DELIVERY_ASSIGNED = 'DELIVERY_ASSIGNED',
  COMPLETED = 'COMPLETED',
  FAILED = 'FAILED'
}
 
@Entity()
export class Order {
  @PrimaryGeneratedColumn('uuid')
  id: string;
 
  @Column({ type: 'decimal', precision: 10, scale: 2 })
  price: number;
 
  @Column({ 
    type: 'enum',
    enum: OrderStatus,
    default: OrderStatus.CREATING
  })
  status: OrderStatus;
 
  @Column({ type: 'text' })
  deliveryAddress: string;
}

3 )消费者服务实现

// order.consumer.service.ts 
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
import { Order, OrderStatus } from './order.entity';
 
@Injectable()
export class OrderConsumerService {
  private readonly logger = new Logger(OrderConsumerService.name);
 
  constructor(
    @InjectRepository(Order)
    private orderRepository: Repository<Order>
  ) {}
 
  @RabbitSubscribe({
    exchange: 'ORDER_EXCHANGE',
    routingKey: 'order.update.*',
    queue: 'ORDER_QUEUE',
    allowNonJsonMessages: false // 强制JSON格式 
  })
  async handleOrderUpdate(message: any) {
    try {
      const dto = message as OrderMessageDTO;
      await this.processOrderStatus(dto);
    } catch (error) {
      this.logger.error(`订单处理异常: ${error.message}`, error.stack);
      // 高级错误处理:死信队列转发 
      throw new DLQRejectError(message);
    }
  }
 
  private async processOrderStatus(dto: OrderMessageDTO) {
    // ...完整状态机实现(包含上文核心逻辑)
  }
}

4 )高可用与错误处理增强

// dlq-handler.service.ts
import { Injectable } from '@nestjs/common';
import { RabbitRPC } from '@golevelup/nestjs-rabbitmq';
 
@Injectable()
export class DLQHandler {
  @RabbitSubscribe({
    exchange: 'DLQ_EXCHANGE',
    routingKey: '#',
    queue: 'DEAD_LETTER_QUEUE',
    queueOptions: {
      messageTtl: 86400000, // 24小时TTL
      deadLetterExchange: '' // 防止循环转发 
    }
  })
  handleDeadLetters(message: any) {
    // 实现报警通知/持久化存储等机制 
    console.error('死信消息:', message);
    // 对接监控系统:Sentry/Datadog等 
  }
}

关键设计要点说明

  1. 状态驱动消息处理
    通过订单状态机(CREATING → RESTAURANT_CONFIRMED → ...)隐式标识消息来源,避免在消息体中添加冗余字段

  2. 消息处理幂等性
    在关键业务节点添加检查逻辑:

    // 防止重复处理 
    if (order.status === OrderStatus.RESTAURANT_CONFIRMED) {
      this.logger.warn(`订单已处理: #${order.id}`);
      return;
    }
    
  3. 分布式事务保障
    采用 Saga 事务模式:

    • 订单服务更新状态后发送消息
    • 骑手服务处理失败时通过补偿消息回滚
    订单服务骑手服务拒绝时商家服务配送请求1确认/拒绝2状态更新3补偿通知4订单服务骑手服务拒绝时商家服务
  4. 消息可靠性增强

    机制实现方式
    生产者确认Publisher Confirm 机制
    消息持久化队列声明+消息属性设置
    消费者ACK手动确认+异常重试
    死信队列失败消息转移+人工介入通道

本实现完整遵循分布式系统设计原则:

  1. 最终一致性:通过消息队列实现跨服务状态同步
  2. 服务自治:各微服务独立维护队列和路由规则
  3. 弹性设计:消息重试/死信队列保障系统韧性
  4. 可观测性:结构化日志+消息轨迹追踪

生产建议:结合 OpenTelemetry 实现全链路追踪,使用 RabbitMQ 的 shovel 插件跨集群同步消息,在流量高峰时启用备用消费者

工程示例:3


1 ) 环境配置与依赖安装

安装必要依赖
npm install amqplib @nestjs/async

2 ) RabbitMQ 连接管理

// src/rabbit/rabbit.module.ts
import { Module } from '@nestjs/common';
import { connect } from 'amqplib';
import { RABBIT_CONFIG } from './rabbit.constants';
 
@Module({
  providers: [
    {
      provide: RABBIT_CONFIG,
      useFactory: async () => {
        const connection = await connect('amqp://localhost');
        return connection.createChannel();
      },
    },
  ],
  exports: [RABBIT_CONFIG],
})
export class RabbitModule {}

3 ) 消息生产与消费全流程

// 生产者示例(商户服务)
import { Inject } from '@nestjs/common';
import { RABBIT_CONFIG } from '../rabbit/rabbit.constants';
import { Channel } from 'amqplib';
 
export class MerchantService {
  constructor(@Inject(RABBIT_CONFIG) private readonly channel: Channel) {}
 
  async sendOrderResponse(orderId: string): Promise<void> {
    this.channel.sendToQueue(
      'order_queue',
      Buffer.from(JSON.stringify({ orderId, status: 'processed' })),
    );
  }
}
 
// 消费者(订单服务) - 补充消息转发至骑手服务 
export class OrderMessageService {
  constructor(@Inject(RABBIT_CONFIG) private readonly channel: Channel) {}
 
  private async forwardToRiderService(content: string): Promise<void> {
    this.channel.sendToQueue('rider_queue', Buffer.from(content));
  }
 
  private processMessage(content: string): void {
    // 解析消息并转发
    const message = JSON.parse(content);
    this.forwardToRiderService(message);
  }
}

4 ) 周边配置处理

配置项说明NestJS 实现
连接重试网络中断时自动重连使用 amqplib 的 reconnect 插件
错误处理消息处理失败时重试或进入死信队列在 consume 回调中添加 try/catch
环境变量管理敏感信息(如密码)从 .env 加载结合 @nestjs/config 模块
监控指标收集消息吞吐量、延迟等数据集成 Prometheus 与 @nestjs/metrics
// 错误处理示例
this.channel.consume('order_queue', async (msg) => {
  try {
    await this.processMessage(msg.content.toString());
    this.channel.ack(msg);
  } catch (error) {
    this.channel.nack(msg, false, false); // 丢弃消息或进入死信队列
    console.error(`Message processing failed: ${error}`);
  }
});

关键优化总结

  1. 语义连贯性:
    • 将分散的线程管理、队列监听、服务启动逻辑整合为 生命周期驱动的初始化流程。
  2. 技术细节补充:
    • 明确 basicConsume 的 noAck 参数 与消息确认机制的关系。
    • 增加 死信队列处理 等生产级方案。
  3. 初学者友好提示:
    • 线程池核心参数(如 corePoolSize)直接影响系统吞吐量。
    • 消费者标签(consumerTag) 用于唯一标识消费者,可实现精细控制。
  4. 工程化扩展:
    • 提供 环境变量集成、监控埋点 等企业级实践。

工程示例:4


1 ) 依赖安装

npm install @nestjs/typeorm typeorm pg 
npm install @golevelup/nestjs-rabbitmq amqplib 

2 ) 主模块配置

// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RabbitMQConfigModule } from './rabbitmq/rabbitmq.module';
 
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'password',
      database: 'restaurant_db',
      entities: [Product, Restaurant],
      synchronize: true 
    }),
    TypeOrmModule.forFeature([Product, Restaurant]),
    RabbitMQConfigModule
  ],
  providers: [OrderHandlerService]
})
export class AppModule {}

3 ) 自动消息确认机制

// 在RabbitMQ装饰器中配置ACK策略 
@RabbitSubscribe({
  exchange: 'order_exchange',
  routingKey: 'order.restaurant',
  queue: 'restaurant_queue',
  allowNonJsonMessages: true,
  queueOptions: { 
    autoAck: true  // 自动确认消息
  }
})

4 ) 异常处理增强

// 在消息处理器中添加异常捕获
async handleOrderMessage(message: OrderMessageDTO) {
  try {
    // ...业务逻辑
  } catch (error) {
    // 记录错误并触发重试机制
    logger.error(`消息处理失败: ${error.message}`);
    throw new RpcException('PROCESSING_FAILED');
  }
}

5 ) 监控与日志集成

// 添加消息处理日志
import { Logger } from '@nestjs/common';
 
const logger = new Logger('OrderHandler');
 
async handleOrderMessage(message: OrderMessageDTO) {
  logger.log(`收到订单校验请求: ${JSON.stringify(message)}`);
  // ...处理逻辑
  logger.log(`订单校验完成: 状态=${message.isConfirmed}`);
}

工程示例: 5


1 ) 订单创建接口实现

// order.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { OrderService } from './order.service';
import { CreateOrderDto } from './dto/create-order.dto';
 
@Controller('orders')
export class OrderController {
  constructor(private readonly orderService: OrderService) {}
 
  @Post()
  async createOrder(@Body() createOrderDto: CreateOrderDto) {
    return this.orderService.create(createOrderDto);
  }
}

2 ) 订单服务层实现

// order.service.ts 
import { Injectable } from '@nestjs/common';
import { RabbitMQService } from './rabbitmq.service';
import { OrderRepository } from './order.repository';
import { CreateOrderDto } from './dto/create-order.dto';
import { OrderStatus } from './enums/order-status.enum';
 
@Injectable()
export class OrderService {
  constructor(
    private readonly orderRepo: OrderRepository,
    private readonly rabbitMQ: RabbitMQService
  ) {}
 
  async create(createOrderDto: CreateOrderDto) {
    // 1. 保存订单到数据库 
    const order = await this.orderRepo.create({
      ...createOrderDto,
      status: OrderStatus.CREATING
    });
 
    // 2. 发送消息到商家微服务 
    const message = {
      orderId: order.id,
      productId: order.productId,
      accountId: order.accountId 
    };
    
    this.rabbitMQ.publish(
      'exchange.order.restaurant', 
      'routing.key.restaurant',
      message
    );
 
    return order;
  }
}

3 )RabbitMQ 服务封装

// rabbitmq.service.ts 
import { Injectable } from '@nestjs/common';
import { connect, Channel, Connection } from 'amqplib';
import { ConfigService } from '@nestjs/config';
 
@Injectable()
export class RabbitMQService {
  private connection: Connection;
  private channel: Channel;
 
  constructor(private configService: ConfigService) {}
 
  async onModuleInit() {
    await this.initialize();
  }
 
  private async initialize() {
    const host = this.configService.get('RABBITMQ_HOST');
    this.connection = await connect(`amqp://${host}`);
    this.channel = await this.connection.createChannel();
    
    // 声明交换机 
    await this.channel.assertExchange('exchange.order.restaurant', 'direct', { durable: true });
    await this.channel.assertExchange('exchange.order.delivery', 'direct', { durable: true });
  }
 
  async publish(exchange: string, routingKey: string, message: any) {
    const messageBuffer = Buffer.from(JSON.stringify(message));
    this.channel.publish(exchange, routingKey, messageBuffer);
  }
}

4 )消息消费者实现

// order.consumer.ts
import { Process, Processor } from '@nestjs/bull';
import { RabbitMQService } from './rabbitmq.service';
import { Job } from 'bull';
import { Logger } from '@nestjs/common';
import { OrderService } from './order.service';
import { OrderStatus } from './enums/order-status.enum';
 
@Processor('order-queue')
export class OrderConsumer {
  private readonly logger = new Logger(OrderConsumer.name);
 
  constructor(
    private readonly orderService: OrderService,
    private readonly rabbitMQ: RabbitMQService 
  ) {}
 
  @Process('restaurant-confirmation')
  async handleRestaurantConfirmation(job: Job) {
    const message = job.data;
    this.logger.log(`收到商家确认消息: ${JSON.stringify(message)}`);
 
    // 1. 验证消息内容 
    if (!message.orderId || !message.confirmed || !message.price) {
      this.logger.error('无效的商家确认消息');
      await this.orderService.updateOrderStatus(message.orderId, OrderStatus.FAILED);
      return;
    }
 
    // 2. 更新订单状态
    await this.orderService.updateOrderStatus(message.orderId, OrderStatus.RESTAURANT_CONFIRMED);
 
    // 3. 通知骑手微服务
    const deliveryMsg = {
      orderId: message.orderId,
      address: message.address,
      estimatedTime: message.estimatedTime 
    };
    
    this.rabbitMQ.publish(
      'exchange.order.delivery',
      'routing.key.delivery',
      deliveryMsg
    );
  }
}

工程示例:6


1 ) 订单服务核心业务流程

当用户创建订单时,系统需完成两大关键操作:持久化订单数据与向商户服务发送消息。
以下是技术实现要点:

// order.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as amqp from 'amqplib';
import { OrderEntity } from './order.entity';
import { OrderCreateDTO } from './dto/order-create.dto';
import { RestaurantMessageDTO } from './dto/restaurant-message.dto';
 
@Injectable()
export class OrderService {
  constructor(
    @InjectRepository(OrderEntity)
    private readonly orderRepo: Repository<OrderEntity>,
  ) {}
 
  async createOrder(orderDto: OrderCreateDTO): Promise<void> {
    // 1. 持久化订单数据 
    const newOrder = new OrderEntity();
    newOrder.address = orderDto.address;
    newOrder.accountId = orderDto.accountId;
    newOrder.productId = orderDto.productId;
    newOrder.status = OrderStatus.CREATING; // 订单创建中状态 
    newOrder.createdAt = new Date();
    
    const savedOrder = await this.orderRepo.save(newOrder); // 保存后自动生成ID
 
    // 2. 构建商户服务消息体 
    const message: RestaurantMessageDTO = {
      orderId: savedOrder.id, // 必须使用数据库生成的ID
      productId: savedOrder.productId,
      accountId: savedOrder.accountId
    };
 
    // 3. 发送消息至RabbitMQ 
    await this.sendToRestaurantService(message);
  }
 
  private async sendToRestaurantService(message: RestaurantMessageDTO): Promise<void> {
    const conn = await amqp.connect('amqp://localhost');
    const channel = await conn.createChannel();
    
    // 关键:使用JSON序列化 
    const messageBuffer = Buffer.from(JSON.stringify(message)); 
    
    await channel.assertExchange('exchange_order_restaurant', 'direct', { durable: true });
    channel.publish(
      'exchange_order_restaurant',
      'routing_key.restaurant', // 商户服务定义的路由键
      messageBuffer,
      { persistent: true } // 消息持久化
    );
    
    await channel.close();
    await conn.close();
  }
}

关键技术细节说明:

  1. ID生成策略:必须使用数据库返回的订单ID(如@PrimaryGeneratedColumn()),而非前端传入的ID
  2. 消息序列化:RabbitMQ仅处理二进制数据,需通过JSON.stringify()和Buffer.from()转换对象
  3. 路由键约定:发送方需与接收方预先约定路由键命名规范(如routing_key.restaurant)

2 ) 订单状态更新与消息响应

商户服务处理完成后,订单服务需监听响应消息并更新状态:

// order.message.service.ts 
import { Delivery } from 'amqplib';
import { ConsumeMessage } from 'amqplib';
import { Injectable } from '@nestjs/common';
 
@Injectable()
export class OrderMessageService {
  async handleDelivery(msg: ConsumeMessage | null): Promise<void> {
    if (!msg) return;
 
    try {
      // 1. 反序列化消息
      const message = JSON.parse(msg.content.toString()) as RestaurantMessageDTO;
      const order = await this.orderRepo.findOne(message.orderId);
 
      // 2. 状态机驱动业务流转 
      switch (order.status) {
        case OrderStatus.CREATING:
          await this.processRestaurantResponse(order, message);
          break;
        case OrderStatus.RESTAURANT_CONFIRMED:
          await this.processDeliveryResponse(order, message);
          break;
        // 其他状态处理...
      }
    } catch (err) {
      this.logger.error(`消息处理失败: ${err.message}`, err.stack);
    } finally {
      channel.ack(msg); // 手动确认消息
    }
  }
 
  private async processRestaurantResponse(order: OrderEntity, message: RestaurantMessageDTO) {
    // 校验商户确认参数 
    if (!message.confirmed || !message.price) {
      order.status = OrderStatus.FAILED;
      await this.orderRepo.save(order);
      return;
    }
 
    // 3. 更新订单状态
    order.status = OrderStatus.RESTAURANT_CONFIRMED;
    order.price = message.price;
    await this.orderRepo.save(order);
 
    // 4. 触发下一阶段(骑手服务)
    await this.notifyDeliveryService(order);
  }
}

状态机设计原则:

  • 使用订单状态作为驱动流转的核心依据(如CREATING → RESTAURANT_CONFIRMED)
  • 每个状态对应明确的业务含义和后续动作
  • 严格校验消息体字段(如confirmed和price)确保业务完整性

3 )消息监听与异步处理

通过声明队列绑定和消费者实现可靠监听:

// order.message.service.ts (续)
class OrderMessageService {
  async startListening() {
    const conn = await amqp.connect('amqp://localhost');
    const channel = await conn.createChannel();
    
    // 声明交换机与队列 
    await channel.assertExchange('exchange_order', 'direct', { durable: true });
    await channel.assertQueue('queue_orders', { durable: true });
    await channel.bindQueue('queue_orders', 'exchange_order', 'routing_key.orders');
    
    // 启动消费者(关闭自动ACK!)
    channel.consume('queue_orders', (msg) => this.handleDelivery(msg), {
      noAck: false // 手动ACK确保可靠性 
    });
  }
}
 
// 在模块初始化时启动监听 
// app.module.ts 
@Module({
  providers: [OrderMessageService],
})
export class AppModule implements OnModuleInit {
  constructor(private readonly messageService: OrderMessageService) {}
  
  onModuleInit() {
    this.messageService.startListening();
  }
}

可靠性增强措施:

  1. 手动ACK:业务处理成功后手动确认消息(channel.ack(msg)),避免消息丢失
  2. 队列持久化:声明队列时设置{ durable: true }防御服务重启
  3. 死信队列:通过deadLetterExchange处理重复失败的消息

工程示例:7


1 ) 方案1:基础 AMQP 连接方案

// 基础连接方法 
async createConnection() {
  const conn = await connect({
    hostname: 'localhost',
    port: 5672,
    username: 'user',
    password: 'pass',
  });
  
  const channel = await conn.createChannel();
  return channel;
}

2 )方案2:NestJS 官方 RabbitMQ 模块

// app.module.ts 
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
 
@Module({
  imports: [
    RabbitMQModule.forRoot(RabbitMQModule, {
      exchanges: [
        {
          name: 'exchange.order.restaurant',
          type: 'direct',
        },
        {
          name: 'exchange.order.delivery',
          type: 'direct',
        }
      ],
      uri: 'amqp://user:pass@localhost:5672',
    })
  ],
  // ...
})
export class AppModule {}

3 )方案3:使用消息队列作业处理器

// delivery.consumer.ts
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
 
export class DeliveryConsumer {
  @RabbitSubscribe({
    exchange: 'exchange.order.delivery',
    routingKey: 'routing.key.delivery',
    queue: 'delivery-queue'
  })
  async handleDeliveryMessage(message: any) {
    // 处理骑手微服务响应 
    const { orderId, deliveryStatus } = message;
    
    if (deliveryStatus === 'ACCEPTED') {
      await this.updateOrderStatus(orderId, OrderStatus.DELIVERY_CONFIRMED);
    } else {
      await this.updateOrderStatus(orderId, OrderStatus.FAILED);
    }
  }
}

关键配置说明

1. 交换机配置:
   - 商家服务:exchange.order.restaurant (direct类型)
   - 骑手服务:exchange.order.delivery (direct类型)
 
2. 路由键约定:
   - 商家服务:routing.key.restaurant 
   - 骑手服务:routing.key.delivery 
 
3. 消息格式规范:
   - 订单创建消息:{ orderId: string, productId: string, accountId: string }
   - 商家响应消息:{ orderId: string, confirmed: boolean, price: number }
   - 骑手通知消息:{ orderId: string, address: string, estimatedTime: number }
 
4. 状态机设计:
   CREATING → RESTAURANT_CONFIRMED → DELIVERY_CONFIRMED → COMPLETED
    └───→ FAILED(任何阶段失败)

关键改进说明


  1. 状态转换验证:使用状态机模式确保订单状态转换的合法性,防止非法状态跳转

    // order.entity.ts
    transitionStatus(newStatus: OrderStatus) {
      const validTransitions = {
        [OrderStatus.CREATING]: [OrderStatus.RESTAURANT_CONFIRMED, OrderStatus.FAILED],
        [OrderStatus.RESTAURANT_CONFIRMED]: [OrderStatus.DELIVERY_CONFIRMED, OrderStatus.FAILED]
      };
      
      if (!validTransitions[this.status].includes(newStatus)) {
        throw new Error(`非法状态转换: ${this.status} → ${newStatus}`);
      }
      this.status = newStatus;
    }
    
  2. 消息持久化:使用数据库事务确保业务操作与消息发送的原子性

    async createOrderTransaction(createOrderDto: CreateOrderDto) {
      await this.entityManager.transaction(async (transactionManager) => {
        const order = transactionManager.create(Order, {...});
        await transactionManager.save(order);
        
        // 消息存入待发送表
        await transactionManager.save(PendingMessage, {
          exchange: 'exchange.order.restaurant',
          routingKey: 'routing.key.restaurant',
          content: JSON.stringify({ orderId: order.id, ... })
        });
      });
    }
    
  3. 死信队列配置:

    // 声明队列时配置死信交换
    await this.channel.assertQueue('order-queue', {
      durable: true,
      deadLetterExchange: 'dlx.order',
      deadLetterRoutingKey: 'order-failed'
    });
    

关键优化点说明


  1. 消息可靠性保障

    • 通过持久化交换机/队列防止服务重启丢失消息
    • 使用ACK确认机制确保消息至少被消费一次
    • 异常重试策略通过@RabbitSubscribe的errorHandler实现
  2. 性能优化

    // 配置连接池提升性能
    RabbitMQModule.forRoot(RabbitMQModule, {
      uri: 'amqp://localhost:5672',
      connectionManagerOptions: {
        heartbeatIntervalInSeconds: 5,
        reconnectTimeInSeconds: 3
      },
      channels: {
        default: { prefetchCount: 10 } // 控制并发量
      }
    })
    
  3. 调试辅助

    • RabbitMQ管理命令:
      # 查看队列状态
      rabbitmqctl list_queues name messages_ready
      # 监控消息流
      rabbitmqctl trace_on
      

工程化注意事项


  1. 连接复用

    // 创建连接池(避免频繁创建销毁)
    const connectionPool = new GenericObjectPool({
      create: () => amqp.connect('amqp://localhost'),
      destroy: (conn) => conn.close(),
      max: 10 // 最大连接数 
    });
    
  2. 错误恢复机制

    channel.on('error', (err) => {
      logger.error('Channel error:', err);
      this.reconnect(); // 实现重连逻辑 
    });
    
  3. 死信队列配置

    // 声明队列时配置死信交换器 
    await channel.assertQueue('main_queue', {
      durable: true,
      deadLetterExchange: 'dlx_exchange',
      deadLetterRoutingKey: 'failed.orders'
    });
    
  4. 消息确认模式

    // 消费者端手动确认 
    channel.consume(queue, (msg) => {
      try {
        processMessage(msg);
        channel.ack(msg); // 处理成功确认 
      } catch (err) {
        channel.nack(msg); // 处理失败拒绝
      }
    });
    

初学者重点概念解析


  • DTO/VO/PO区别

    • VO(View Object):前端与控制器交互对象
    • DTO(Data Transfer Object):服务间传输对象
    • PO(Persistent Object):数据库映射实体
  • 消息队列核心概念

    对齐方式语法示例语法示例
    Exchange消息路由中心direct/topic/fanout
    Routing Key消息路由路径标识符‘restaurant.order’
    Queue消息存储队列durable: true
    Binding交换器与队列的映射关系channel.bindQueue()
  • 序列化选择依据

    • JSON:跨语言兼容性强,可读性高
    • Protocol Buffers:高性能二进制协议
    • Avro:支持Schema演进

容错设计关键点

  1. 数据一致性保障

    • 发件箱模式:确保DB更新与消息写入原子性
    • 消费端幂等:通过event_id或order_id+status去重
    • 事务补偿:对超时未处理事件启动补偿作业(Scan Job)
  2. 消息可靠性

    • Kafka特性:启用acks=all保证Broker持久化
    • 重试策略:
      发送失败
      5s后重试
      30s后重试
      死信队列+告警
  3. 性能优化

    • 发件箱批量处理(每批100-200事件)
    • 骑手分配使用CQRS模式:分离查询与命令,缓存骑手实时位置

异常场景处理


场景解决方案
商家重复确认数据库唯一索引拦截(order_id+status组合索引)
消息队列宕机消息中继服务暂停,积压事件由补偿作业处理
骑手服务不可用消息重试+降级策略(转人工调度)
网络分区基于Quorum的写一致性(如Cassandra发件箱存储)

该设计通过事务消息+异步解耦实现高吞吐(实测支持>3000TPS),同时保证业务数据最终一致性。骑手微服务可通过gRPC流式接口进一步优化实时性需求

异常处理机制示例

// 增强的消息发布方法 
async publishWithRetry(
  exchange: string,
  routingKey: string,
  message: any,
  retries = 3
) {
  try {
    await this.publish(exchange, routingKey, message);
  } catch (err) {
    this.logger.error(`消息发布失败: ${err.message}`);
    if (retries > 0) {
      setTimeout(() => {
        this.publishWithRetry(exchange, routingKey, message, retries - 1);
      }, 3000);
    } else {
      this.logger.error(`消息最终发送失败: ${JSON.stringify(message)}`);
      // 持久化到数据库进行补偿
      await this.saveFailedMessage(message);
    }
  }
}

知识扩展:关键概念解析


  1. Direct Exchange
    路由模式中,消息通过精确匹配routingKey投递到队列,适用于点对点场景。

  2. TypeORM 活跃记录 vs 数据映射器
    示例采用Repository模式(数据映射器),分离实体与持久化逻辑,更符合DDD原则。

  3. 消息序列化选择
    NestJS默认使用JSON序列化,可通过自定义Serializer支持Protocol Buffers等二进制协议。

  4. 事务边界设计
    在分布式系统中,需通过Saga模式协调跨服务事务,避免使用分布式事务(XA协议)。

延伸知识:RabbitMQ通信模式选择


模式特性微服务间通信适用性
Fanout广播到所有绑定队列低(易引发循环)
Direct按RoutingKey精确路由高(推荐点对点)
Topic通配符匹配路由中(复杂路由场景)
Headers基于消息头路由低(较少使用)

最佳实践:服务间点对点通信优先使用 Direct Exchange + 唯一RoutingKey,避免Fanout广播导致的意外循环消费

错误处理加固建议

// 消息消费的健壮性增强
@MessagePattern('settlement_queue')
async handleMessage(@Payload() message: any, @Ctx() context: RmqContext) {
  const channel = context.getChannelRef();
  try {
    // ...业务逻辑 
    channel.ack(message); // 确认消息 
  } catch (error) {
    channel.nack(message, false, true); // 重试
  }
}

关键配置:

  • noAck: false:关闭自动确认
  • 死信队列:处理超过重试次数的消息
  • 持久化:Exchange/Queue声明时添加durable: true

总结

通过分离Exchange解决循环消息问题,验证了RabbitMQ通信模型设计的核心原则:

  1. 隔离性:服务间通信需物理隔离Exchange通道
  2. 定向性:避免广播模式用于点对点场景
  3. 可观测性:通过管控台实时验证绑定关系
    最终实现四微服务(订单→餐厅→骑手→结算)的稳定协同,为后续积分服务扩展奠定基础
订单系统开发 一、订单系统基本框架的搭建 1、创建maven工程 pom.xml文件内容如下: project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/PO 阅读详情

相关推荐

RabbitMQ与Kotlin:现代JVM语言的消息处理

在当今分布式系统架构中,可靠的消息传递已成为构建弹性应用的关键支柱。本文深入探讨了如何将RabbitMQ——这个功能强大的消息代理——与Kotlin这门现代JVM语言相结合,打造高效、可靠且易于维护的消息处理系统。我们将从基础概念讲起,逐步深入到高级应用模式,通过丰富的代码示例和实际案例,展示这一技术组合如何解决分布式系统中的通信挑战。无论你是消息队列新手还是有经验的开发者,本文都将为你提供从理论到实践的全面指导,帮助你在项目中充分发挥RabbitMQ与Kotlin的协同优势。

架构师的AI之路,分享AI应用开发架构的学习与实践。 1191

RabbitMQ 实战:电商下单流程

通过以上步骤,我们实现了一个基于 RabbitMQ 的电商下单流程,展示了如何通过消息队列实现异步处理与服务之间的解耦。同时,采用幂等性设计和消息可靠性机制,确保了系统的稳定性与可靠性。这种架构能够有效提高系统的响应速度,并降低系统间的耦合度,使得各个服务可以独立扩展和维护。

weixin_51052174的博客 2326

SpringBoot外卖点餐系统实战:从架构设计到性能优化

在现代Web应用开发中,SpringBoot作为Java领域的主流框架,以其约定大于配置的理念,极大地简化了企业级应用的开发与部署流程。其核心原理在于通过自动配置和起步依赖,快速整合Spring生态组件,使开发者能聚焦于业务逻辑实现。这一技术价值在于显著提升了开发效率与系统可维护性,尤其适用于电商、外卖等高并发在线交易场景。以构建一个外卖点餐系统为例,系统需处理用户认证、订单流转、实时库存等复杂业务。通过引入Redis缓存高频数据如菜单信息,可有效降低数据库压力,提升响应速度;同时,利用RabbitMQ消息

weixin_31309419的博客 288

使用rabbitmq处理订单系统

订单系统能够快速响应用户支付请求,并将订单信息发送到 RabbitMQ 后即可返回响应给用户,而不需要等待邮件和短信的发送完成。消费者应用可以根据自身的处理能力和负载情况,适时地处理订单消息,保证系统的可扩展性和稳定性。整个订单系统的流程在步骤 1 到步骤 9 的基础上,引入了 RabbitMQ 来处理订单消息的异步通知。订单系统将订单信息通过 RabbitMQ 发送到消息队列中,然后消费者应用连接到 RabbitMQ,订阅队列并接收订单消息,执行相应的业务逻辑,如发送订单确认邮件和短信通知给用户。

m0_56668216的博客 1605

excel wind插件使用_Python和wind的交互—在债券中的应用

个人认为学会了怎么用python和wind交互以及怎么用python处理Excel数据,就基本能替换日常很多琐碎的债券工作,还不用担心Excel某个值改变了导致整个报表都毁了。事实上在python调用wind函数和在Excel中调用函数一样简单,只要会基本的操作就够了。下面详细的介绍如何用python和wind进行交互。首先安装好python+pycharm+wind。因为python...

weixin_39863918的博客 5332

别再挖空PCB了!集成变压器RJ45网口布局的3个关键细节(附AD/Altium Designer实战图)

本文深入解析集成变压器RJ45网口在PCB设计中的关键布局细节,包括禁布区设置、差分线布线技巧及PoE设计考量。通过Altium Designer实战案例,帮助工程师优化信号完整性与结构强度,避免传统挖空设计的误区,提升工业级以太网设备的可靠性。

weixin_33728268的博客 560

使用RabbitMQ死信队列和延迟队列模拟订单系统使用场景

在日常购物平台中,如果某个订单超过30分钟还未支付成功,系统就会将该订单取消,并进行库存回滚,本实例就使用RabbitMQ提供的两大特性:**延迟队列和死信队列** 来模拟这一应用场景,但实际上RabbitMQ并没有提供真正意义上的延迟队列,这句话什么意思呢?简单来讲就是RabbitMQ任意队列都没有方法直接指定消息被消费的时间(一般消息到达队列时,就会立马被路由到与之绑定的消费者,而不是说该消息到队列之后,等待30分钟再路由到消费者,目前RabbitMQ是不支持这种功能的),但是我们可以通过设置消息的一个

个人学习博客,不足之处,还望不吝赐教! 916

【AutoSar CP】SoAd模块

比如对于特定的PDU,其PduId在程序编译时就确定了,但是在代码运行中,该PduId可能需要发送到不同的远端ip,这时就需要用SoAd来设置连接远端地址信息。SoAdSocketConnectionGroup(连接参数)SoAdSocketRoute(接收数据)SoAdRoutingGroup(开关)SoAdPduRoute(发送数据)

至道无难,唯嫌拣择。 1690

RabbitMQ学习笔记

RabbitMq消息队列

weixin_52938172的博客 1841

从零构建PEMFC仿真模型:MATLAB/Simulink中的模块化拆解与系统集成

本文详细介绍了如何在MATLAB/Simulink环境中从零构建质子交换膜燃料电池(PEMFC)仿真模型,包括电化学模型、气体供应系统和热管理系统的模块化实现。通过多物理场耦合技巧和系统集成策略,帮助工程师和研究者高效开发PEMFC控制策略,优化性能并实现实时仿真。

weixin_29294975的博客 362

VMware + ubuntu20.04 + qemu安装

遇到报错可参考:(从ubuntu裸系统开始安装)

NosONE 3232

Java从零实现国际版扫码点餐系统:架构设计与踩坑总结

在餐饮数字化浪潮中,扫码点餐已成为连接顾客与商家的核心入口。看似简单的“扫码-下单-支付”流程,一旦跨越国界,便面临多语言菜品模型、多币种金额精度、多时区时间归因、跨境支付回调幂等等复杂挑战。Java凭借成熟的生态与稳定的框架,成为构建此类高并发业务系统的理想选择。基于Spring Boot 3.x单体架构,详细拆解国际版扫码点餐的实战要点,包括订单状态机设计、Redis缓存与Lua脚本扣库存、RabbitMQ异步事件驱动、以及支付网关适配等关键技术。从菜单多语言表的结构设计到分布式部署的运维坑点,系统性地

weixin_34354173的博客 172

YOLOv8改进 | 主干网络 | 在backbone添加Swin-Transformer层【论文必备】

YOLOv8改进,Swin-Transform,YOLOv8添加Swin-Transform

kay_545 2367

RabbitMQ基础知识以及如何构建集群

rabbitMQ的一些知识

m0_60697978的博客 710

使用 ABAP Development Tool 登录 SAP BTP ABAP 环境时,遇到弹出框应该怎么办?

但在实际环境里,隐私模式、第三方 Cookie 禁止、跨站追踪限制、缓存代理误命中、混合内容拦截等因素,都可能让这条跳转链路被打断。等),浏览器会弹出系统原生登录框,请求用户输入账号和口令或做集成认证。只要它们在响应头里出现,浏览器就会理直气壮地弹框,这是协议层的强约定。,最终弹出原生登录框。前端与网关领域有个通用的经验规则:如果你看到了原生登录框,而期望的是带样式的。环境里的登录方式表述为基于浏览器的前端认证,也就是这类重定向式的交互。环境的具体落地,拆出几类常见诱因与排障路径,并穿插真实世界的案例。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 375

[VT虚拟化驱动]正式启动VT

文章目录前言一、vmlaunch正式开始虚拟化二、使用步骤1.引入库2.读入数据总结 前言 本章开始真正实现一个最简单的VT框架,当然,只是能跑起来一个VT驱动了,没有并包括EPT,EPT后面再讲 提示:以下是本篇文章正文内容,下面案例可供参考 一、vmlaunch正式开始虚拟化 执行VMLAUNCH指令执行后,就正式进入虚拟化了,再该指令执行之前,得先保存Guest当前的环境,执行完VMLAUNCH后,要恢复Guest的环境,让Guest就像什么都没发生一样,所以需要保存和恢复Guest的各种寄存器的值

吾无法无天的博客 3663

传菜窗口的智慧——从阿明的“前后厨大混乱“,看消息队列与异步架构的设计哲学

摘要: 本文是「阿明餐厅」系列的第11篇正传,探讨微服务架构中同步调用的弊端及消息队列的解决方案。阿明餐厅的系统从5个服务扩展到20个微服务后,同步调用的级联故障、延迟叠加等问题凸显,导致订单成功率暴跌。文章通过餐厅类比,分析了同步调用的“七宗罪”,并对比了Kafka、RabbitMQ等消息队列的选型策略。最后提出消息可靠性的三板斧(生产端确认、持久化存储、消费端ACK),确保关键业务消息不丢失。 关键词: 微服务、消息队列、异步通信、Kafka、RabbitMQ、可靠性保障

wb04307201的博客 215

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

保姆级教程:SpringBoot 3.x整合SpringCloud Stream,搞定消息分组消费和持久化(附避坑指南)

本文详细介绍了SpringBoot 3.x整合SpringCloud Stream实现消息分组消费和持久化的完整解决方案。通过实战案例和避坑指南,帮助开发者解决微服务架构中的消息重复处理和可靠性投递问题,提升电商平台订单系统等场景下的消息处理效率。

weixin_30437847的博客 124

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

并行分布式框架:Celery、rq、funboost

Celery是一个异步任务的调度工具,也可以叫做 "分布式任务队列(Distributed Task Queue)"。

freeking101的博客 5万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

RabbitMQ 延迟队列实现订单支付结果异步阶梯性通知

在第三方支付中,例如支付宝、或者微信,对于订单请求,第三方支付系统采用的是消息同步返回、异步通知+主动补偿查询的补偿机制。 由于互联网通信的不可靠性,例如双方网络、服务器、应用等因素的影响,不管是同步返回、异步通知、主动查询报文都可能出现超时无响应、报文丢失等情况,所以像支付业务,对结果的通知一般采用几种方案结合的补偿机制,不能完全依赖某一种机制。 例如一个支付结果的通知,一方面会在支

nongfuyumin的专栏 8793

Java秒杀系统实战系列~RabbitMQ死信队列处理超时未支付的订单

摘要:本篇博文是“Java秒杀系统实战系列文章”的第十篇,本篇博文我们将采用RabbitMQ的死信队列的方式处理“用户秒杀成功生成订单后,却迟迟没有支付”的情况,一起来见识一下RabbitMQ死信队列在实际业务环境下的强大之处!内容:对于消息中间件RabbitMQ,Debug其实在前面的篇章中已经简单分享介绍过了,在这里就不再赘述了!在本文我们将采用RabbitMQ的死信队列实现这样的业务需求:“...

weixin_30583563的博客 1253

RabbitMQ的应用场景以及基本原理介绍

1.背景RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue)的开源实现。2.应用场景2.1异步处理场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种1.串行的方式;2.并行的方式 (1)串行方式:将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有一个问题是,邮件,短信并不是必须的,它只是一个

杨龙飞的博客 20万+

SpringCloud+RabbitMQ微服务订单系统

1.下单的时候,调用订单微服务系统的下单方法,首先,先给订单实体赋值,然后在向MQ发送消息,接收到MQ返回来的消息后,保存订单,在生成微信或支付宝的签名字符串 2.下单(购买)接口 @PostMapping("/payGoods") public Result payGoods(@RequestBody OrderSGoods orderSGoods, HttpServletRequ...

路在路上的博客 2747

RabbitMQ 简介以及安装使用

RabbitMQ:消息队列

yaya_jn的博客 1523

(六) RabbitMQ实战教程(面向Java开发人员)之RabbitMQ消息的可靠性

消息可靠性在项目中使用RabbitMQ时,我们可能会遇到这样的问题:如一个订单系统当用户付款成功时我们往消息中间件添加一条记录期望消息消费者修改订单状态,但是最终实际订单状态并没有被修改成功。遇到这种问题我们排查的思路如下:1.消息是否已经成功发送到消息中间件 2.消息是否有丢失的情况 消息是否已经被消费成功在生产环境下是不容许出现消息投递/消费错误的情况的,因为这可能会对企业产生巨大的损失,本博客

黄太洪的博客 3375

SpringBoot 集成 rabbitmq 简单实现通过队列进行,订单系统与库存系统,物流系统之间的信息交互...

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34413357的博客 2089
上一篇: RabbitMQ: 基于 Direct Exchange 的微服务通信与数模设计
下一篇: RabbitMQ: Topic Exchange 的积分微服务和工程要点
Wang's Blog
博客等级 码龄12年 7438粉丝 2422原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

Wang's Blog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值