架构 (IT)系统架构师

调解一个行星规模的异构消息中介,连接传统企业消息总线(**IBM MQ**,**TIBCO Rendezvous**)与云原生事件流平台(**Apache Kafka**,**AWS EventBridge**),确保金融交易命令的精确一次处理语义,基于队列深度遥测实现自主适配器弹性,并在混合网络拓扑中保证有毒消息隔离而不阻塞头部消息?

用 Hintsage AI 助手通过面试

问题的答案

问题历史

企业现代化计划越来越需要将数十年前的IBM MQTIBCO基础设施与Apache KafkaAWS EventBridge集成,而无需重写遗留COBOL主机。金融服务特别要求交易命令的精确一次语义,因为重复执行构成实质风险和监管违规。

问题

遗留消息总线缺乏本地幂等性原语,并依赖于具有破坏性读取的强制性FIFO排序,而云原生流更倾向于基于偏移量的不可变日志重播。协议阻抗不匹配——固定宽度COBOL拷贝本与自描述Avro——结合异构交付保证,在适配器扩展事件或临时网络分区期间产生消息丢失或重复的矢量。

解决方案

Kubernetes中部署运行Apache CamelSpring Cloud Stream的无状态协议适配器使用IBM MQ队列深度指标来介导系统之间的交互。使用RedisAmazon DynamoDB实现幂等消费者模式,以跟踪处理的消息UUID,并设置TTL过期。利用具有read_committed隔离级别的Kafka事务确保原子偏移量提交和消息生产。根据通过Prometheus导出的IBM MQ队列深度指标使用KEDA(Kubernetes事件驱动自主伸缩)自动扩展适配器。将有毒消息隔离到在Amazon SQSApache Pulsar中实现的**死信队列(DLQ)**以防止头部阻塞。

生活中的情况

某家顶级投资银行需要在不影响服务的情况下将实时交易执行流程从运行IBM MQz/OS主机迁移到AWS MSK(Kafka)。遗留系统发布了表示买入/卖出订单的COBOL拷贝本编码消息,而现代Java微服务消费Avro序列化事件。在市场波动期间,消息速率激增至50,000 TPS,导致初始桥接实现因TCP缓冲区大小不足和缺乏背压而丢失消息。

解决方案 1: 双写与调和。 该方法修改主机以同时写入IBM MQApache Kafka,然后进行夜间调和作业修复差异。优点包括基础设施变化最小和快速实施时间。缺点在于日内交易间违反了精确一次语义,调和滞后造成监管审计问题,以及冲突解决的人工干预要求违反了自动化SLO。

解决方案 2: 存储与转发与XA事务。 实现WebSphere MQ作为协调与Kafka事务生产者的X/Open XA资源管理器跨越两阶段提交边界。优点是通过原子承诺协议提供强一致性。缺点包括在跨区域复制时在WAN链接上保持的锁持有毫秒,从而造成阻塞行为违反了低于100毫秒的延迟SLO,以及XA驱动程序与类似AWS MSK的托管Kafka产品的不兼容。

解决方案 3: 无状态协议桥与外部去重。 部署Apache Camel桥作为Kubernetes部署,使用动态JRecord解析器将COBOL转换为Avro,并在生产到Kafka之前根据DynamoDB进行唯一的UUID检查。KEDA根据MQSC命令报告的队列深度扩展副本。优点包括非阻塞的横向可扩展架构和通过幂等性而非分布式交易实现的精确一次。缺点需要对于DynamoDB容量规划和Camel路由监控的操作成熟度。

选定解决方案及结果。 选择了解决方案3以维持低于50毫秒的端到端延迟。在模拟黑五交易量的压力测试中,该系统处理了250万条消息,零重复和零丢失。当出现格式错误的消息(缺少强制CUSIP字段)时,断路器Resilience4j)开启,将坏消息转发至Amazon SQS DLQ,同时允许合法交易流动,防止初始试点期间的灾难性积压。

候选人常常忽视的问题

当遗留MQ缺乏消息去重且Kafka消费者因偏移量提交失败可以重新处理消息时,如何维护精确一次语义?

候选人常常仅建议Kafka幂等生产者,这只能解决Kafka内部的去重,而不是跨越MQ到Kafka边界的去重。正确的方法结合了源系统的收件箱模式——其中主机在其DB2数据库中事务性地将消息写入收件箱表,然后像Debezium这样的CDC(变更数据捕获)连接器将更改流式传输到Kafka——以及消费者侧的去重存储(Redis SETNXDynamoDB条件写入)。消费者在执行业务逻辑时将UUID以原子方式写入存储,确保即使在消费者重新平衡或分区重新分配期间也保持幂等性。

如何在不重新部署协议适配器桥的情况下处理COBOL拷贝本模式演进?

大多数候选人建议使用类似CB2XML的工具从COBOL拷贝本静态代码生成,每次模式变化都需要重新部署。一个稳健的解决方案使用运行时模式解析:将拷贝本定义存储在GitAWS S3中,在消息头中通过版本ID引用。Apache Camel路由使用JRecord通过动态类加载来解析基于头部指定的模式版本的消息。结合Kubernetes ConfigMapAWS AppConfig热加载,在不重新启动副本的情况下刷新模式。这将主机发布周期与云部署管道解耦。

当云目标的长期停机导致遗留MQ队列达到最大深度时,如何防止MQ存储有限?

候选人经常建议无限缓冲或MQ磁盘扩展,这只是延迟必然发生。正确的策略实现背压卸载:配置IBM MQ 应用程序消息路由MQIPT(MQ互联网穿透)以在队列深度超过80%时触发阈值警报。桥接停止读入(施加背压),并切换到存储与转发模式,将传入消息写入Amazon S3Azure Blob Storage作为序列化文件。连接恢复后,Sidecar容器使用AWS SDK多部分上传将S3对象重播到Kafka,消除积压,而不会造成MQ磁盘耗尽或消息丢失。