java System Message Queue 简要说明

本文深入探讨了发布/订阅消息传送模型,介绍了其核心组件——发布者、订户和主题,以及消息如何在这些组件间广播。文章详细解释了消息的发布、接收、过滤和保留机制,并讨论了该模型在动态系统中的应用。

发布/订阅消息传送

在发布/订阅域中,消息生成方被称为发布者,而消息使用方则被称为订户。它们通过称为主题的目的地来交换消息:发布者生成主题中的消息;订户则订阅主题并使用主题中的消息。

图 2–3 显示了发布/订阅域中的简单消息传送操作。MyTopicPublisher 向目的地 MyTopic 发布 Msg1。然后,MyTopicSubscriber1和 MyTopicSubscriber2 均从 MyTopic 接收 Msg1 的副本。

图 2–3 简单发布/订阅消息传送


图中显示了通过一个主题目的地向两个订户发送同一条消息的一个发布者。该图用文本进行说明。

 

虽然发布/订阅模型不要求多个订户,但图中仍显示了两个订户来强调通过此域可以广播消息。一个主题的所有订户均可获得发布到该主题的任何消息的副本。

长期订户可能处于活动状态,也可能处于非活动状态。代理会为所有活动订户保留消息,但对于非活动订户,则只为那些长期订户保留消息。

图 2–4 显示了更为复杂的发布/订阅消息传送图,以说明该模式提供的可能情况。多个生成方向 Topic1 目的地发布消息。多个订户使用来自 Topic1 目的地的消息。除非订户使用选择器来过滤消息,否则每个订户均可获得发布到所选主题的所有消息。在图 2–4 中,MyTSubscriber2 已过滤掉 Msg2。

图 2–4 复杂发布/订阅消息传送


图中显示了通过一个主题目的地向三个订户发送消息的三个发布者。该图用文本进行说明。

 

这张较为复杂的图说明了有关发布/订阅消息传送的很多其他点。

  • 多个生成方可向一个主题发布消息。生成方可共享连接或使用不同连接,但它们均可访问同一主题。

  • 多个订户可使用一个主题中的消息。订户可检索发布到一个主题中的所有消息,除非它们使用选择器过滤掉消息或消息在使用之前已过期。

  • 订户可共享连接或使用不同连接,但它们均可访问同一主题。

  • 长期订户可能处于活动状态,也可能处于非活动状态。在它们处于非活动状态时,代理会为它们保留消息。

  • 可在运行时动态添加和删除发布者和订户,这样,即可根据需要缩放消息传送系统。

  • 消息按发送的顺序发布到主题,但使用消息的顺序取决于多种因素,例如消息失效日期、消息优先级以及使用消息时是否使用了选择器。

  • 发布者与订户之间存在时间上的相关性:主题订户只能使用在它创建订阅后发布的消息。

发布/订阅模型的最大优点是消息可以广播给订户。

### Kafka 实现死信队列的最佳实践 在分布式系统中,死信队列(Dead Letter Queue, DLQ)用于存储无法正常处理的消息。对于 Apache Kafka 而言,虽然其本身并未提供原生支持的死信队列功能,但可以通过一些设计模式和最佳实践来实现这一目标。 #### 方法一:手动重试与转发至DLQ 当消费者接收到消息后发生不可恢复错误时,可将该消息转发到专门的死信队列主题中。以下是具体实现方式: 1. **配置消费者监听器** 在消费者的逻辑中捕获异常,并判断是否为永久性错误。如果是,则将消息发送到指定的死信队列主题。 2. **代码示例** 下面是一个简单的 Java 示例,展示如何将失败的消息写入死信队列: ```java import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; public void processMessage(ConsumerRecord<String, String> record, KafkaProducer<String, String> producer) { try { // 处理业务逻辑 handleBusinessLogic(record); } catch (Exception e) { System.out.println("Processing failed for message: " + record.value()); sendToDlq(record, producer); // 将失败消息发送到DLQ } } private void sendToDlq(ConsumerRecord<String, String> record, KafkaProducer<String, String> producer) { ProducerRecord<String, String> dlqRecord = new ProducerRecord<>("dlq-topic", record.key(), record.value()); producer.send(dlqRecord); } ``` 3. **注意事项** - 需要在 `sendToDlq` 中设置合适的分区键以保持顺序[^1]。 - 死信队列应具有独立的主题名称以便于管理和排查问题。 --- #### 方法二:基于时间窗口的自动丢弃机制 如果某些消息经过多次尝试仍然未能成功处理,可以设定一个最大重试次数或超时时间,在达到条件后将其移至死信队列。 1. **引入状态管理** 使用外部数据库或者内存缓存记录每条消息的重试计数以及最后处理的时间戳。 2. **伪代码描述** 假设每次消费都更新消息的状态表,若超过预定义阈值则转入DLQ: ```pseudo function consume(message): retryCount = getStateFromCache(message.id) if retryCount >= MAX_RETRIES or isExpired(message.timestamp): forwardToDlq(message) else: incrementRetryCountInCache(message.id) attemptProcess(message) ``` 3. **优点与缺点** 这种方法的优点在于能够自动化处理部分场景下的失败情况;然而它增加了系统的复杂度,可能带来额外开销。 --- #### 方法三:利用Kafka Streams构建自定义流程 借助 Kafka 的流处理 API 可以更灵活地控制消息流转路径。例如,可以根据特定条件动态决定某条消息进入常规通道还是跳转至DLQ。 1. **核心概念** 创建两个分支拓扑结构——一个是正常的业务链路,另一个则是针对异常情形准备的备用出口。 2. **简化版架构图** ``` Input Topic -> Processor A/B/C -> Output Topic | Diverge v Dead-Letter-Queue ``` 3. **实际编码片段** 利用 DSL 或 Processor API 构建复杂的过滤规则: ```java streamsBuilder.stream("input-topic") .filter((key, value) -> isValid(value)) // 合法校验 .to("output-topic"); streamsBuilder.stream("input-topic") .filterNot((key, value) -> isValid(value)) .mapValues(v -> enrichErrorInfo(v)) // 补充上下文信息 .to("dlq-topic"); ``` 4. **适用范围** 对于需要高度定制化需求的应用尤为适合,比如涉及多阶段验证、跨域关联等情况。 --- ### 总结说明 无论采用哪种方案,都需要综合考虑性能影响、运维便利性和扩展能力等因素。通常情况下推荐优先选用简单明了的手动转发策略作为入门级选项,待积累一定经验后再逐步探索高级特性如Streams框架的支持[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值