JAVA面试:消息队列


1. 项目为何选择MQ?使用场景与重复消费处理

项目为何选择MQ?使用场景与重复消费处理

核心价值与场景

  • 解耦系统:生产者与消费者独立演化(如企业微信事件变更消息、(添加好友、打标签等),客户端埋点采集——埋点分析系统)。
  • 异步处理:耗时操作(如短信通知,批量操作:批量打标、批量删除、客户人群计算、客户群发结果推送)异步化,提升主流程响应速度。
  • 削峰填谷:应对瞬时高并发(如消息入库、埋点数据入库、秒杀场景、生产者增多),通过MQ缓冲请求。
  • 事务一致性:订单支付后扣减库存(事务消息保障最终一致性)。
    重复消费解决方案
  • 业务幂等性设计:
    • 唯一业务ID:如数据库唯一索引、Redis分布式锁(Redisson)。
    • 状态机控制:订单状态仅允许特定操作执行一次。
  • RocketMQ原生机制:
    • 消息Key+Check:生产者设置唯一Key,消费者查询Redis是否存在该Key。
    • 手动提交Offset:消费成功后手动ACK,避免宕机导致重复拉取。

2.消息队列核心存储结构详解

存储结构

RocketMQ 存储结构

  • CommitLog

    • RocketMQ 的核心存储物理文件,采用顺序写入方式
    • 所有 topic 的消息都写入同一个 CommitLog 中,提升写入性能、吞吐量
    • 通过 MappedByteBuffer 实现内存映射,提高 I/O 性能
    • 文件大小默认为 1GB,写满后自动创建新的 CommitLog 文件
  • ConsumeQueue

    • 为每个 topicqueueId 维护的逻辑队列
    • 记录消息在 CommitLog 中的物理位置、大小和 tag 的哈希值
    • 每条记录固定 20 字节,便于消费者快速定位消息
    • 支持基于 tag 的消息过滤机制
  • IndexFile

    • 可选的索引文件,支持根据消息 Key 或时间范围进行消息检索
    • 采用哈希索引+链表解决冲突的设计
    • 支持快速定位特定 key 对应的消息位置

Kafka 存储结构

  • Partition

    • topic 的分区结构,是 Kafka 并行处理的基本单位
    • 每个 partition 对应一个 log 目录,目录命名规则为 topicName-partitionId
    • 支持水平扩展,通过增加 partition 数量提升并发处理能力
  • Segment

    • 每个 partition 由多个 segment 文件组成
    • 包含 .log 数据文件和 .index 索引文件
    • 默认 segment 大小为 1GB,可通过配置调整
    • 支持基于时间或大小的 segment 切分策略
  • 顺序写入

    • 消息在 partition 内按顺序追加写入
    • 充分利用操作系统页缓存机制,减少磁盘 I/O
    • 通过零拷贝技术提升读取性能
  • 稀疏索引

    • 通过 .index 文件维护消息偏移量与物理位置的映射关系
    • 采用稀疏索引减少索引文件大小,平衡查询效率和存储开销

RabbitMQ 存储结构

  • Queue

    • 队列是 RabbitMQ 的核心存储单元
    • 每个 queue 有独立的存储结构,支持多种存储后端
    • 内置消息确认机制,确保消息不丢失
  • 消息持久化

    • 支持将消息持久化到磁盘,通过 msg_storequeue_index 等文件管理
    • msg_store 存储实际消息内容
    • queue_index 维护队列中消息的索引信息
  • 内存与磁盘

    • 支持内存存储(高性能)和磁盘存储(高可靠性)两种模式
    • 可配置消息何时从内存刷入磁盘
    • 支持 lazy queue 模式,将消息尽可能保存在磁盘上
  • 镜像队列

    • 集群模式下通过 mirroring 机制实现队列复制
    • 提供高可用性保障,防止单点故障
    • 支持同步和异步两种镜像模式

3. 零丢失与消息不重复保障

保障机制

三种消息队列的零丢失与消息不重复保障机制

RocketMQ 保障机制

零丢失保障

  • 生产者端:
    • 同步刷盘模式确保消息写入磁盘
    • 主从同步复制确保消息在多个节点备份
    • 发送失败重试机制
  • Broker端:
    • CommitLog 顺序写入保证数据持久性
    • 多副本机制防止单点故障
    • 宕机恢复机制确保未刷盘数据不丢失
  • 消费者端:
    • 消费确认机制(ACK)确保消息被正确处理
    • 消费位点持久化防止重复消费

消息不重复保障

  • 幂等性设计:
    • 提供 UNIQ_KEY 保证消息唯一性
    • 消费端需要实现幂等处理逻辑
  • 消费状态管理:
    • ConsumeQueue 记录消费状态
    • 支持跳过重复消息机制

Kafka 保障机制

零丢失保障

  • 生产者端:
    • acks=all 配置确保消息被所有副本接收
    • 重试机制处理发送失败
    • 事务支持确保消息原子性
  • Broker端:
    • 多副本机制(replication.factor)
    • ISR(In-Sync Replicas)机制保证数据一致性
    • 定期刷盘确保数据持久化
  • 消费者端:
    • 手动提交偏移量确保消息被正确处理
    • enable.auto.commit=false 关闭自动提交

消息不重复保障

  • 幂等性生产者:
    • enable.idempotence=true 开启生产者幂等性
    • producer.id + sequence.number 防止重复发送
  • 事务支持:
    • transactional.id 确保跨会话幂等性
    • Exactly-Once 语义保证

RabbitMQ 保障机制

零丢失保障

  • 生产者端:
    • mandatory 参数确保消息路由到队列
    • 发布确认机制(publisher confirms)
    • 事务机制保证消息发送可靠性
  • Broker端:
    • 队列持久化(durable=true)
    • 消息持久化(deliveryMode=2)
    • 镜像队列防止节点故障数据丢失
  • 消费者端:
    • 手动确认模式(autoAck=false)
    • 消息重回队列机制

消息不重复保障

  • 消费者幂等性:
    • 需要在应用层实现幂等处理
    • 使用消息ID去重
  • 死信队列:
    • 处理重复消费和异常消息
  • 事务机制:
    • 确保消息发布和业务操作的原子性

总结对比

特性 RocketMQ Kafka RabbitMQ
零丢失 强依赖刷盘和主从同步 依赖acks=all和ISR 依赖持久化和确认机制
不重复 需应用层幂等 生产者幂等+事务 需应用层幂等
配置复杂度 中等 较高 中等
性能影响 中等 较小 较大

4.线上MQ常见问题及详细解决方案(深度详解)

常见问题

1. 消息丢失问题详解

问题根本原因分析

  • 生产者端: 网络异常、Broker无响应、配置不当导致消息未成功发送
  • Broker端: 内存消息未及时刷盘、主从同步失败、硬件故障
  • 消费者端: 消费后未正确ACK、消费者异常退出导致消息回滚

RocketMQ详细解决方案

生产者端保障:

// 生产者配置示例
DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
producer.setRetryTimesWhenSendFailed(3);  // 同步发送失败重试3次
producer.setRetryTimesWhenSendAsyncFailed(3);  // 异步发送失败重试3次
producer.setSendMsgTimeout(3000);  // 发送超时时间3秒

Broker端保障:

# broker.conf 配置
flushDiskType=SYNC_FLUSH          # 同步刷盘确保数据持久化
brokerRole=SYNC_MASTER            # 同步主从模式
haHousekeepingInterval=20000      # HA连接健康检查间隔20秒
waitTimeMillsInHeartbeatQueue=31000  # 心跳队列等待时间

内部运行机制:

  1. SendMessageProcessor 处理消息发送请求时,根据 flushDiskType 决定是否等待刷盘完成
  2. CommitLog 写入完成后触发 GroupCommitService 进行刷盘确认
  3. 主从同步通过 HAConnection 实现,主节点等待从节点确认后才返回成功

Kafka详细解决方案

生产者端配置:

Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all");                    // 等待所有副本确认
props.put("retries", 3);                     // 重试3次
props.put("enable.idempotence", true);       // 开启幂等性
props.put("max.in.flight.requests.per.connection", 1);  // 单连接请求数限制

Broker端配置:

# server.properties 配置
min.insync.replicas=2              # 最少同步副本数
unclean.leader.election.enable=false  # 禁止非ISR节点成为leader
replication.factor=3               # 副本因子
log.flush.interval.messages=10000  # 每1万条消息刷盘一次
log.flush.interval.ms=1000         # 每秒刷盘一次

内部运行机制:

  1. ISR(In-Sync Replicas)列表维护可用副本,只有ISR中的副本才能参与leader选举
  2. HW(High Watermark)标记已确认的消息边界,消费者只能读取到HW之前的消息
  3. LEO(Log End Offset)跟踪每个副本的最新偏移量位置

RabbitMQ详细解决方案

生产者保障:

// 启用发布确认机制
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        // 消息确认成功处理
    }
    
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        // 消息确认失败处理,需要重发
    }
});

Broker配置:

%% rabbitmq.conf 配置
queue_master_locator=min-masters     % 队列master节点选择策略
cluster_partition_handling=pause_minority  % 集群分区处理策略
disk_free_limit.absolute = 1GB       % 磁盘空间阈值

2. 消息重复消费问题详解

问题产生场景

  • 网络抖动导致ACK未送达Broker
  • 消费者处理完成后进程意外终止
  • Broker故障恢复后重新投递未确认消息

应用层幂等性处理方案

基于数据库唯一约束:

@Service
public class OrderService {
    
    @Transactional
    public void processOrder(String orderId, String orderData) {
        try {
            // 使用订单号作为唯一约束,重复插入会抛出异常
            Order order = new Order();
            order.setOrderId(orderId);
            order.setData(orderData);
            orderRepository.save(order);  // 数据库唯一约束保证幂等
            
            // 处理订单逻辑
            processBusinessLogic(order);
        } catch (DataIntegrityViolationException e) {
            // 订单已存在,说明是重复消费,直接返回成功
            log.info("Order {} already processed, skipping...", orderId);
        }
    }
}

基于Redis去重:

@Service
public class MessageDeduplicationService {
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    public boolean isDuplicate(String messageId) {
        String key = "dedup:message:" + messageId;
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(key, "1", Duration.ofHours(24));
        return !result;  // true表示已存在(重复),false表示首次处理
    }
    
    public void markAsProcessed(String messageId) {
        String key = "dedup:message:" + messageId;
        redisTemplate.opsForValue().set(key, "1", Duration.ofHours(24));
    }
}

RocketMQ去重机制

  • 在 RocketMQ 4.3 版本之后引入了事务消息机制,这在某种程度上可以解决“发送端”的重复问题,或者配合半消息机制实现类似 Exactly-Once 的效果。
  • 机制: 生产者先发送一个“半消息”(Half Message),消费者不可见。等本地事务执行成功后,再发送一个“提交”指令,消息才可见。
  • 去重作用: 这种方式主要防止了因生产者网络问题导致的重复发送,从源头上减少了重复消息的产生。

3. 消息积压问题详解

积压原因分析

  • 消费者处理能力不足
  • 突发大量消息涌入
  • 消费者异常导致消费停滞
  • 网络或系统资源瓶颈

扩容解决方案

  • 必须确认的前置条件:
  • 监控指标:消费者处理队列长度、CPU使用率、消息积压量、消费延迟
  • 扩容阈值:当单节点消费能力达到瓶颈(CPU>70%、队列持续增长)时启动扩容
  • 资源评估:确保新增实例有足够资源(CPU、内存、网络带宽)
Kafka消费者扩容问题与解决方案
  • 🚨 主要问题
    • Rebalance导致消费暂停:新增消费者触发分区重新分配,所有消费者暂停消费
    • 分区数限制瓶颈:消费者实例数不能超过Topic分区数,否则多余实例闲置
    • 消息顺序性破坏:扩容后分区分配变化可能导致消息乱序
    • 重复消费风险:Rebalance期间可能触发消息重试

🛠️ 解决步骤

  1. 检查分区数是否充足
  • 确认当前Topic分区数:
kafka-topics.sh --describe --topic your-topic
  • 若分区不足:需先扩容分区(需谨慎,可能影响顺序性)
kafka-topics.sh --alter --topic your-topic --partitions 32
  1. 调整消费者配置
# 增加消费线程数
num.consumer.threads=8
# 增大单次拉取量(根据消息大小调整)
max.poll.records=500
# 延长会话超时时间,减少误判
session.timeout.ms=10000
  1. 优化Rebalance过程
# 减少Rebalance频率
max.poll.interval.ms=300000
# 增加心跳间隔
heartbeat.interval.ms=3000
  • 使用Cooperative协议(Kafka 2.4+)
    粘性协议
partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor
  1. 监控扩容效果
  • 关键指标:consumer lag、poll latency、process latency
  • 使用Kafka自带监控工具:
kafka-consumer-groups.sh --describe --group your-group
RabbitMQ消费者扩容问题与解决方案
  • 🚨 主要问题
    • 竞争消费者模式导致负载不均:多个消费者竞争同一队列,处理能力不一
    • 消息顺序性难以保证:多消费者同时处理消息,无法保证顺序
    • 资源浪费:过多消费者导致连接数增加,但无法提升并行度
    • 预取机制导致"慢消费者"阻塞:prefetch_count设置不当
  • 🛠️ 解决步骤
    • 调整消费者配置
    • 优化预取数量(关键):
spring.rabbitmq.listener.simple.prefetch=10 # 避免过大导致慢消费者阻塞
  • 设置合理并发度:
spring.rabbitmq.listener.simple.concurrency=5 # 初始并发数
spring.rabbitmq.listener.simple.max-concurrency=5 # 最大并发数
  • 拆分队列实现负载均衡
  • 按业务Key拆分队列:
// 按订单ID分流
String routingKey = "order." + orderId;
channel.basicPublish("exchange", routingKey, null, message.getBytes());
  • 创建多个队列:将原队列拆分为多个子队列,每个消费者处理一个队列
RocketMQ消费者扩容问题与解决方案
  • 🚨 主要问题
    • 队列粒度负载均衡限制:每个MessageQueue只能被一个消费者消费
    • 消费者数量超过队列数导致资源浪费:多余消费者闲置
    • Rebalance期间消息重复消费:队列重新分配时可能触发重试
    • 消息顺序性破坏:扩容后队列分配变化
  • 🛠️ 解决步骤
    • 检查队列数是否充足
    • 若队列不足:需先扩容Topic队列数
# 创建新Topic(临时)
mqadmin updateTopic -t temp-topic -n localhost:9876 -c DefaultCluster -r 32
  • 临时扩容方案(消息积压严重时)
    创建临时Topic:将原Topic队列数扩容至10倍
    开发临时消费程序:
// 消费原Topic消息,转发到临时Topic
@RocketMQMessageListener(topic = "original-topic", consumerGroup = "temp-consumer")
public class TempConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        // 将消息均匀转发到临时Topic的各队列
        rocketMQTemplate.send("temp-topic", message);
    }
}

  • 部署新消费者:消费临时Topic,处理能力提升10倍

  • 调整消费者配置:


// 增加消费线程数
consumer.setConsumeThreadMin(20);
consumer.setConsumeThreadMax(50);
// 开启批量消费
consumer.setConsumeMessageBatchMaxSize(32);
  • 确保幂等性:必须实现消息去重,避免重复消费
通用最佳实践
  • 扩容前必须确认的问题
    • 是否真的需要扩容:先排查消费慢的原因(如慢SQL、外部依赖问题)
    • 是否有足够队列/分区:确保扩容后能充分利用新增消费者
    • 是否已实现幂等性:避免扩容导致的重复消费问题
  • 扩容实施策略
    • 逐步扩容:每次增加1-2个实例,观察系统稳定性
    • 错峰扩容:在业务低峰期进行扩容操作
    • 灰度发布:先部署少量新实例,验证无问题后再全面扩容
  • 扩容后验证
    • 监控关键指标:消费延迟、队列长度、系统资源使用
    • 检查消息顺序性:确保业务逻辑不受影响
    • 验证幂等性:确认无重复消费问题
  • 预防性措施
    • 设置自动扩缩容:基于队列长度和消费延迟自动调整消费者数量
    • 建立监控告警:提前发现潜在的消费瓶颈
    • 定期压测:评估系统最大处理能力,提前规划扩容
  • 重要提醒:扩容不是万能药,应优先优化单点消费能力(如调整线程池、优化SQL、减少外部依赖调用)。只有当单点能力达到瓶颈且队列/分区资源充足时,才应考虑增加消费者实例。同时,扩容前必须确保已实现消息幂等性,否则可能导致严重的业务问题。

流量控制方案

基于令牌桶的限流:

@Component
public class MessageRateLimiter {
    
    private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String topic) {
        RateLimiter limiter = limiters.computeIfAbsent(topic, 
            k -> RateLimiter.create(1000.0));  // 每秒1000个消息
        return limiter.tryAcquire();
    }
    
    public void consumeWithRateLimit(List<MessageExt> msgs, Consumer<List<MessageExt>> processor) {
        List<MessageExt> allowedMsgs = msgs.stream()
            .filter(msg -> tryAcquire(msg.getTopic()))
            .collect(Collectors.toList());
        
        if (!allowedMsgs.isEmpty()) {
            processor.accept(allowedMsgs);
        }
    }
}

4. 网络异常和连接问题详解

连接异常诊断

心跳检测配置:

RocketMQ:

# 客户端心跳配置
heartbeatBrokerInterval=30000           # 心跳间隔30秒
persistConsumerOffsetInterval=5000      # 消费位点持久化间隔
clientCallbackExecutorThreads=8         # 回调线程数

Kafka:

# 客户端连接配置
connections.max.idle.ms=540000          # 连接最大空闲时间9分钟
request.timeout.ms=30000                # 请求超时时间30秒
session.timeout.ms=45000                # session超时时间45秒
heartbeat.interval.ms=3000              # 心跳间隔3秒

RabbitMQ:

ConnectionFactory factory = new ConnectionFactory();
factory.setRequestedHeartbeat(60);      // 心跳间隔60秒
factory.setConnectionTimeout(60000);    // 连接超时60秒
factory.setHandshakeTimeout(10000);     // 握手超时10秒

连接池管理

RocketMQ连接池优化:

public class OptimizedMQProducer {
    
    private DefaultMQProducer producer;
    
    public void init() {
        producer = new DefaultMQProducer("optimized_producer_group");
        producer.setSendMessageThreadPoolNums(16);    // 发送线程数
        producer.setPullMessageThreadPoolNums(64);    // 拉取消息线程数
        producer.setClientCallbackExecutorThreads(8); // 回调线程数
        producer.start();
    }
    
    public SendResult send(Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        return producer.send(msg, 3000);  // 3秒超时
    }
}

5. 性能瓶颈问题详解

JVM调优参数

RocketMQ Broker JVM参数:

-Xms4g -Xmx4g                          # 堆内存4G
-XX:+UseG1GC                           # 使用G1垃圾收集器
-XX:G1HeapRegionSize=16m               # G1区域大小
-XX:MetaspaceSize=256m                 # 元空间大小
-XX:MaxDirectMemorySize=4g             # 直接内存大小

Kafka Broker JVM参数:

-Xms4g -Xmx4g                          # 堆内存4G
-server                                # 服务器模式
-XX:+UseG1GC                           # G1垃圾收集器
-XX:MaxGCPauseMillis=20                # 最大GC暂停时间20ms
-XX:InitiatingHeapOccupancyPercent=35  # 触发GC的堆占用百分比

磁盘I/O优化

RocketMQ磁盘优化:

# store.conf 配置
storePathRootDir=/data/rocketmq/store   # 存储路径
storePathCommitLog=/data/rocketmq/store/commitlog  # CommitLog路径
mappedFileSizeCommitLog=1073741824      # CommitLog文件大小1GB
deleteWhen=04                           # 每天凌晨4点删除过期文件
fileReservedTime=72                     # 文件保留72小时

Kafka磁盘优化:

# server.properties 配置
log.dirs=/data/kafka/logs               # 日志存储目录
num.recovery.threads.per.data.dir=2     # 每个数据目录恢复线程数
log.retention.hours=168                 # 日志保留7天
log.segment.bytes=1073741824            # 段文件大小1GB
log.retention.check.interval.ms=300000  # 检查保留策略间隔5分钟

6. 监控和告警详解

核心监控指标

RocketMQ监控指标:

  • rocketmq_producer_tps: 生产者TPS
  • rocketmq_consumer_tps: 消费者TPS
  • rocketmq_broker_commitlog_disk_ratio: CommitLog磁盘使用率
  • rocketmq_consumer_diff: 消费积压数量

Kafka监控指标:

  • kafka_server_broker_topic_metrics_messages_in_total: 消息流入总量
  • kafka_server_broker_topic_metrics_bytes_in_total: 字节流入总量
  • kafka_consumer_group_lag: 消费者组滞后量
  • kafka_server_replica_manager_under_replicated_partitions: 未充分复制的分区数

RabbitMQ监控指标:

  • rabbitmq_queue_messages_ready: 队列中就绪消息数
  • rabbitmq_queue_messages_unacknowledged: 未确认消息数
  • rabbitmq_channel_consumers: 消费者数量
  • rabbitmq_node_disk_free: 节点磁盘剩余空间

监控实现示例

@Component
public class MqMetricsCollector {
    
    private final MeterRegistry meterRegistry;
    
    public MqMetricsCollector(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    public void recordMessageDelay(String topic, long delayMs) {
        Timer.builder("message.delay")
            .tag("topic", topic)
            .register(meterRegistry)
            .record(delayMs, TimeUnit.MILLISECONDS);
    }
    
    public void recordConsumerLag(String consumerGroup, String topic, long lag) {
        Gauge.builder("consumer.lag")
            .tag("group", consumerGroup)
            .tag("topic", topic)
            .register(meterRegistry, lag);
    }
    
    public void incrementErrorCount(String errorType) {
        Counter.builder("mq.errors")
            .tag("type", errorType)
            .register(meterRegistry)
            .increment();
    }
}

7. 故障排查和恢复详解

日志分析技巧

RocketMQ故障排查:

# 查看Broker日志中的关键错误
grep -i "error\|exception\|warn" rocketmqlogs/broker.log | tail -100

# 查看消息轨迹
grep "UNIQ_KEY" rocketmqlogs/store.log | grep "your_message_key"

# 检查网络连接
netstat -anp | grep :9876  # namesrv端口
netstat -anp | grep :10911 # broker端口

Kafka故障排查:

# 查看Kafka日志
tail -f /opt/kafka/logs/server.log

# 检查消费者组状态
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group your-group

# 查看topic详情
./kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic your-topic

自动恢复机制

健康检查实现:

@Component
public class MqHealthChecker {
    
    @Scheduled(fixedRate = 30000)  // 每30秒检查一次
    public void checkHealth() {
        try {
            // 检查MQ连接状态
            if (!isMqHealthy()) {
                // 触发告警
                alertService.sendAlert("MQ connection unhealthy");
                
                // 尝试重连
                reconnectMq();
            }
        } catch (Exception e) {
            log.error("Health check failed", e);
        }
    }
    
    private boolean isMqHealthy() {
        // 实现具体的健康检查逻辑
        try {
            // 发送测试消息并等待响应
            Message testMsg = new Message("HEALTH_CHECK_TOPIC", "ping".getBytes());
            SendResult result = producer.send(testMsg, 3000);
            return result.getSendStatus() == SendStatus.SEND_OK;
        } catch (Exception e) {
            return false;
        }
    }
}

这些详细的解决方案涵盖了线上MQ系统遇到的各种典型问题,通过合理的配置、代码实现和监控手段,可以有效保障消息系统的稳定性和可靠性。


5. RabbitMQ与RocketMQ核心差异

维度 RocketMQ RabbitMQ
存储模型 CommitLog+ConsumeQueue混合存储 内存+磁盘混合,支持镜像集群
事务支持 原生事务消息(两阶段提交) 依赖插件或外部事务管理
顺序消息 全局/分区顺序支持 仅队列内部顺序
吞吐量 同步刷盘约10万TPS 万级TPS(依赖镜像模式)
适用场景 金融交易、高吞吐场景 路由复杂、低延迟场景

6. 幂等性与零丢失实现

  • 幂等性:
    • 数据库唯一索引:拦截重复业务键。
    • Redis幂等令牌:消费前SETNX锁,成功则处理。
  • 零丢失:
    • Dledger副本:基于Raft协议自动切换主节点。
    • Checkpoint机制:记录刷盘时间戳,故障恢复时重放日志。

7. 选择RocketMQ而非Kafka的原因

  • 事务与顺序需求:金融场景依赖事务消息与严格顺序。
  • 企业级支持:阿里云提供全链路监控与故障自愈。
  • 灵活过滤:支持SQL表达式过滤(如a > 5 AND b = 'hello')。

8. 确保消息不重复的机制

  • 生产者去重:全局SequenceId+本地缓存。
  • 消费者幂等:数据库乐观锁(版本号控制)。

9. 消息积压处理

  • 扩容Consumer:增加实例数至Queue数量一致。
  • 批量消费:调整maxBatchSize提升吞吐量。

10. 消息重复根源

  • 网络波动导致生产者重试。
  • 消费者处理超时触发Broker重投。
  • 主从切换后未同步消息。

11. Rebalance流程与触发

  • 触发条件:消费者上下线、队列数变更。
  • 分配策略:平均分配队列(如10队列→3消费者分配3-4-3)。

12. 文件存储与CommitLog映射

  • CommitLog:消息按顺序追加到1GB文件,文件名标识起始偏移量。
  • ConsumeQueue:每条记录CommitLog偏移量、消息大小及Tag哈希,通过逻辑偏移定位消息。

13. NameServer功能

  • 路由管理:维护Topic→Broker映射表,客户端定时拉取(30秒)。
  • 故障剔除:Broker 30秒无心跳则从路由表移除。

14. RocketMQ核心特性

  1. 高吞吐:顺序写+内存映射(Mmap)减少I/O损耗。
  2. 灵活过滤:支持SQL表达式与Tag哈希过滤。
  3. 多副本高可用:Dledger基于Raft协议实现自动主从切换。
  4. 企业级监控:提供Dashboard与Prometheus指标集成。

总结
RocketMQ通过事务消息+同步刷盘+主从同步+手动ACK组合实现零丢失,结合幂等性设计+Rebalance优化保障消息可靠。其混合存储模型与企业级事务支持使其在金融、电商等强一致性场景优于Kafka。实际部署需根据业务SLA权衡性能与可靠性(如同步刷盘降低吞吐量)。

posted @ 2025-07-04 16:35  爪哇搬砖  阅读(11)  评论(0)    收藏  举报