写点什么

京东消息中间件 JMQ:架构,与 Kafka 的对比,主要特性和应用场景

  • 2016-10-12
  • 本文字数:3619 字

    阅读完需:约 12 分钟

JMQ 是京东自主研发的一款消息中间件系统,具有高可用、数据高可靠等特性。广泛应用于公司内部系统,包括订单、支付、库房等场景。

整体结构

系统包括服务端、客户端、管理端与其他支撑模块。

服务端

服务端提供了配置信息分发、重试消息管理和消息存储与分发这三大类功能。每个服务端实例都具备这三类功能的服务能力,但是在实际部署上这三类功能对应三个不同的集群,对应每一个实例功能不叠加。在测试环境和库房等资源有限的环境下,这三类功能由同一个服务端实例提供服务。

配置信息分发:负责客户端参数变更时与消息分配的服务端实例变更时通知客户端。

重试消息管理:主要用于对业务系统临时处理不了的消息进行存放,然后再按照一定的策略投递给客户端处理。可以提供错误原因、错误处理次数等查询。

消息存储与分发:接收生产者投递的消息,把消息存放在本地磁盘上,消费者从该服务上拉取消息进行消费。

客户端

目前只提供了 JAVA 语言的 SDK 和支持 HTTP 协议的 proxy,非 JAVA 语言通过 proxy 接入。

管理端

主要功能有:接入申请、消息元数据管理、重试消息信息查询、消息发送和消费日志查询、服务端状态信息管理查看、客户端连接信息管理查看等。

支撑模块

主要有报警模块、任务模块、归档模块、信息采集模块等。

数据可靠性

针对公司的业务特点,消息服务主要应用于订单、支付、物流等环节。服务端采用 MASTER-SLAVE 结构,消息在正常情况下会同时存放两份,其中一份会强制持久化到磁盘,磁盘做 RAID-5。默认情况下客户端采用同步发送,每条消息到达服务端 MASTER 后会强制刷入磁盘同时并行推送一份到 SLAVE 上,SLAVE 写入文件系统后不等待强制刷盘就反馈给 MASTER。根据不同的场景为了提高服务的可用性,普通级别的消息 SLAVE 断开后,该组服务可以正常使用,当 SLAVE 连接上后又会自动切换为保存两份。当然对数据可靠级别高的消息是强制要求数据必须写两份才算成功的。

服务高可用

每类消息一般都会分配 3 组及以上的服务组,每组服务包括一个 MASTER 和一个 SLAVE,当然如果有需要也可以挂载多个 SLAVE。

客户端发送消息时,如果其中一组出现故障会重试发送给其他的组。

虽然 MASTER-SLAVE 支持切换,提高服务的可用性,但是在实际生产中 MASTER 出现故障时会优先采用通过其他服务组自动接替生产服务的方式,本组服务只提供从 SLAVE 读取的方式,而不是让 SLAVE 接替 MASTER 的写入,避免临界状态下丢失消息。

对要求严格顺序的消息,不能通过简单的切换服务组实现,具体实现方式参考《高可用保证消息绝对顺序消费的 BROKER 设计方案》(点击“阅读原文”查看这篇文章)。

消费模型

由于公司以前有使用基于 ACTIVEMQ 二次开发的服务,服务端会存放客户端的消费位置,因此在自主研发 JMQ 时也延续了这种方式(可以兼容 ACTIVEMQ 的客户端)。但是 ACTIVEMQ 生产和消费都会操作索引文件,影响性能,JMQ 吸取了这个经验教训。消费者在消费时按照索引分区顺序的消费,消费确认时只需要变更最后确认位置的值,不需要操作索引文件,而且多个消费者共用一个索引文件,各自保存自己的消费偏移位置就可以了。

当然在实践过程中,由于一些特殊场景需要,会允许一定范围内不完全按照顺序消费,但是服务端会记录已经消费的索引区间。

与 KAFKA 的对比

JMQ 在服务端存储设计上与 KAFKA 有一些相似的地方,借鉴了文件按照偏移位置管理、顺序追加等特点。不过 JMQ 的存储和消费模型有自己的特点:

消息存放

JMQ 每个存储系统只有一个分段存储的日志文件,不同类的消息按照服务端接收的顺序存放在日志文件中,通过索引程序按照不同的消息(主题)分类名异步创建各自的索引,方便消费端获取消息时快速定位该客户端所关心的(主题)分类消息。每个(主题)分类的索引划分了多个分区,同一(主题)分类的消息分配在多组服务器上的分区数是相同的。每个索引分区都是以链表按照时间序存放消息引用信息。

消费

JMQ 也采用客户端主动拉取的方式,但是客户端不需要协调自己应该从哪个服务器上获取消息,服务端会控制好每个索引分区里对应的消息在同一时刻只会被一个客户端线程取走,直到客户端反馈消费成功或者消费异常,消费异常会被重试程序转移到重试服务中。如果客户端长时间没有反馈信息,达到了超时时间,那么锁定的消息可以被其他的线程拉取走。

由于服务端储存了每个消费者消费的位置,因此服务器可以随时把已经消费的消息移除走。

主要特性与场景

发布与订阅

目前公司接入的消息绝大部分都采用这种方式,不同类的消息通过主题名进行区分,多个消费者分组之间各自消费一份完整的消息内容,他们看到的消费视图一模一样,唯一的区别就是各自消费进度不同。

同一个消费分组内的消费实例只会消费到其中一部分消息,各自连接服务端,通过抢占的方式进行消费。

场景:

以订单消息为例,订单系统在订单的生命周期里的每一次变更都会发送消息,订单查询系统、结算系统、库房生产系统等都会订阅该类型的消息,每个系统拿到一份完整的消息,各自进行处理。

广播

由于发布订阅型的主题消息,如果要获取一份完整的消息就需要命名一个消费组,如果一类消息每个消费者实例都需要获取一份完整的消息,如果还按照主题消息管理那么就需要为每一个实例命名一个唯一的标识,使用时非常不方便,这时可以使用广播类型的消息,每个消费广播消息的实例都会拿到一份完整消息。

场景:

分布式数据库接入层对应的服务端拓扑信息需要调整,客户端可以订阅一个拓扑变更的广播消息,提前把需要变更的拓扑信息下发给每个客户端备用,当捕捉到拓扑变更的异常后就启用备用拓扑信息。

顺序消费

消息的消费会根据服务端接收到的顺序,依次推送给客户端消费,消息如果乱序可能会引起最终结果不正常。

场景:

数据库 binglog 日志基于消息系统进行复制,接收到消息的客户端可以更新 ElasticSearch 中的索引信息,可以修改 Redis 中的值,同时也可以基于日志重放同步数据到一个全量的数据库中。如果有一条记录的更新和删除操作乱序到达消费端,那么各个系统的状态将会不一致。

索引分区并行消费

默认情况下,每个索引分区的消息只能够按照顺序依次进行消费,如果索引分区内有一条消息处理比较慢,就会阻塞后面消息的处理,导致消息积压,影响消息的实时性。为了解决这个问题,可以增大索引分区数,但是每个索引分区对应独立的文件夹,增大会导致文件夹数目扩大,而且不能根本解决,只是一定程度缓解积压的消息数目。如果让单个索引分区内的消息可以并行的把不同区间的消息发送给客户端处理,这样如果有某条消息处理慢,服务端可以把后面的消息交给空闲的客户端线程去处理,当连续多个区间的消息都消费后再统一合并为一个大的消费区间,减少服务端需要记录的已消费区间数。

场景:

有一个通过消息派发任务的应用,每个任务执行时间长短不一,消费端获取到消息后,根据消息构建任务执行,任务完成后反馈给服务端消费成功。由于任务执行时间长短不一样,因此客户端的超时时间只能以最长的时间为参考进行设置,避免任务在执行过程中由于超时被其他线程重复处理。但是当一个时间相对长的任务在执行时,它会占用该消息所在索引分区被锁定,后面的任务不能及时派发给空闲的客户端处理。这时服务端如果启用索引分区并行消费的特性,就可以及时的把后面的任务派发给其他的客户端去执行,同时也不需要调整索引的分区数。

事务消息

事务消息具有回滚的特点,当消息发送给服务端未提交前,如果关联的其他业务操作失败,客户端可以主动发起回滚,当回滚或者提交事务消息时网络故障,消息系统会主动调用客户端的事务状态查询接口,根据客户端查询到的事务状态决定消息是否提交或回滚。这样就能够保证消息系统和业务系统数据状态最终完全一致。利用消息系统会主动查询不确定状态消息的特点,可以做为多个资源的事务协调器使用。

场景:

变更缺货商品的库存信息时,需要更新下单系统中的库存数,需要通知搜索系统修改商品索引,需要通知网页缓存系统刷新。各个系统之间由于各种网络或服务等原因造成状态不一致。可能出现库存变更了,其他系统的商品可销售状态没有修改正确,或者出现库存数据修改失败,但是其他系统的商品状态发生了变更。只能通过一些核对系统定期的把各个系统中的不一致状态变更为一致,加大了开发工作量,而且定期扫描可能引发性能问题。

通过事务消息,可以很好的解决这类场景,不会因为网络不可用等原因出现系统之间状态不一致。

当更新任何一个服务出现故障时就抛出异常,事务消息不会被提交或回滚,消息服务器会回调发送端的事务查询接口,确定事务状态,发送端程序可以根据消息的内容对未做完的任务重新执行,然后告诉消息服务器该事务的状态。

作者介绍

丁俊, 有 10 年工作经验,目前就职于京东商城云平台,为消息中间件研发小组 leader,主要负责公司内部高性能、高可用消息中间件的架构。


感谢 杜小芳对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-10-12 17:1111051
发现更多内容

【MindStudio训练营第一季】MindStudio 高精度对比随笔

Angel Wings

MindStudio

面试官:你先回去等通知吧!这个Java岗位我还有机会吗?

钟奕礼

Java java面试 java编程 程序员、

连阿里P8都赞不绝口的“分布式架构原理设计笔记”到底有多牛

小小怪下士

Java 程序员 分布式 架构设计

深入React源码揭开渲染更新流程的面纱

goClient1992

React

大数据培训程序员就业前景如何

小谷哥

金融信创正当时!看这家银行如何实现数据仓库与营销平台国产替换?

索信达控股

数据库 银行 数据库迁移 智能营销

HummerRisk 入门3:开发手册

HummerCloud

云安全 云原生安全 12月月更

2023来袭,分享500道我收集的Java架构面试题

钟奕礼

Java 程序员 java面试 java编程

Log4Shell 漏洞披露已近一年,它对我们还有影响吗?

SEAL安全

Log4Shell 12 月 PK 榜

参加前端培训之后能做什么?

小谷哥

前端培训学习后程序员有什么好的方向吗

小谷哥

【MindStudio训练营第一季】MindStudio Profiling随笔

Angel Wings

华为 AI 调优 MindStudio Ascend

手写一个react,看透react运行机制

goClient1992

React

15年磨一剑,亚马逊云科技数据产品掌门人 Swami 揭秘云原生数据战略的三大关键要素

亚马逊云科技 (Amazon Web Services)

亚马逊云科技

大数据培训学习的技术基础有哪些

小谷哥

【MindStudio训练营第一季】MindStudio 可视化AI应用开发体验随笔

Angel Wings

MindStudio

可观测性神器之Micrometer

宋小生

监控 可观测性 micrometer

觅健牵手博鳌医院战略合作:即时优享全球肿瘤创新治疗方案

联营汇聚

双机互备是什么意思?双机互备就是双机热备吗?

行云管家

双机热备 双机互备

带你实现react源码的核心功能

goClient1992

React

36kr企服点评启发:“信任的基础是真实,只要做到真实,一定能够建立起双向信任”

B Impact

2022年第三季度汽车品牌智能网联竞争力指数(ICVCI)分析

易观分析

汽车 易观分析 智能网联

信息安全等级保护的5个级别分别是什么?最高级别是哪个?

行云管家

等保 等级保护

从今天起真正释放创造力 | Werner Vogels 在 re:Invent 2022带来多项开发者福音

亚马逊云科技 (Amazon Web Services)

亚马逊云科技

react源码分析:组件的创建和更新

flyzz177

React

【MindStudio训练营第一季】MindStudio 专家系统随笔

Angel Wings

mindspore MindStudio

手机银行APP评测系列:天津银行持续优化手机银行用户体验,但仍需加强细节提升

易观分析

手机银行 用户

为什么我们不支持手工上传镜像

Jianmu

Docker 容器 镜像

隐藏复杂、抽象概念,「技术无感化」 ——The Future of Database2022 | 黄东旭新番

B Impact

一个月面试了3家大厂Java岗,我发现这几个突破点

钟奕礼

Java 程序员 java面试 java编程

盘点 | 4类爆款小游戏开发引擎

FinFish

游戏引擎 小游戏 小游戏开发 微信小游戏 游戏开发引擎

京东消息中间件JMQ:架构,与Kafka的对比,主要特性和应用场景_语言 & 开发_丁俊_InfoQ精选文章