本文作者:丁威 - 中通快递资深架构师,《RocketMQ技术内幕》作者,Apache RocketMQ社区首席布道师,公众号「中间件兴趣圈」维护者。

01 物流行业的业务特点

物流行业有三大业务特点:业务量庞大、实效性容忍度高以及业务复杂度极高。

作为快递行业龙头企业,中通的日均订单量早已高达5000 万,双11期间可达日均到2亿+,日均消息流转超过万亿。

在快递行业的实际日常业务中,比如早上10点下单,可能需要下午2点钟揽件,因此我们能够容忍分钟级甚至小时级的延迟。针对包裹拦截、路由变更等场景需要保证一定的时效性,但大多情况下只需尽可能保证即可。

中通快递为加盟制,其转运中心、分布中心以及网点都不在一家公司,因此业务逻辑、管控、结算等都较为复杂。

快递行业的业务系统结算、订单、运单采集等等对解耦性要求很高。此外,双十一期间流量可能为平时的3-4倍,因此也需要应对突发流量的能力。而RocketMQ的能力与我们的业务场景非常吻合,因此在快递行业的应用也是极为广泛。

02 RocketMQ在订单中心运用案例

中通基于RocketMQ构建订单中心的架构图如上。

用户在天猫或淘宝用电商平台进行下单后,订单会派至云服务器,中通自研的数据同步平台负责定位云服务器的变动日志,通过变动日志将其传入RocketMQ。

第二层链路为拼多多和京东等厂家,通过网关进入RocketMQ。

第三层为散件用户,也是通过网关进入RocketMQ。

流量到达RocketMQ以后,由订单域的消费者消费 消费者消息并写入数据库。后续的很多系统比如运单域、结算都需要这份数据,为了解耦,需要通过同步平台将数据在同步到另外topic,供各个业务系统进行订阅以及数据分发。

上述架构的关键在于如何保证 MQ 服务器性能与数据可靠性。比如 MQ 使用的刷盘策略是同步还是异步?是否开启transientStorePoolEnable提高性能?复制策略是同步还是异步?是否需要Dledger? 如何优雅运维?

RocketMQ在4.5.0版本之前即支持主从同步,4.5.0版本之后引入Dledger多副本机制,支持了主从切换。一个复制组内有主节点和从节点,不同复制组之间负载均衡。通常情况下由主节点承担数据的读和写,当主节点较为繁忙时,读取可转发到从节点上。同时如果主节点故障,从节点依然能进行消费,以保证消息发送和消息消费的高可用。

一个复制组故障后,请求会全部打到另外的复制组上,导致其因流量过大而出现故障。因此,在实际环境中一般建议部署成四个复制组,以应对流量暴增的情况。

RocketMQ 4.5.0之后引入了Dledger多副本机制,支持主从切换,不同复制组之间依然负载均衡。主节点负责读和写,从节点只负责复制数据。当复制组内主节点宕机后,会在该复制组内出发重新选主,选主完成后即可继续提供消息写功能,不会将流量转移到别的复制组,保证了发送和消费的高可用。

但Dledger多副本机制依然存在缺点,三台机器中只有主节点才能读和写,从节点只负责切换,主节点承担了较大压力。如果能让从节点也承担读写请求,按照主从同步模型,主节点宕机后将请求转换到从节点上,才能实现真正的高可用。

物流行业一般选择主从同步模式,因为主从切换的意义不大,而且浪费机器。

用户发送消息时,消息会先到broker master,然后存储到PageCache,再用同步或异步的方式写磁盘。为了保证消息绝对不丢,会使用同步刷盘,同时将数据复制到从节点。一份数据在多个地方存储,能够避免单点故障导致数据丢失。

Dledger模式是基于Raft协议的数据副本机制,要求复制组内超过一半的节点成功写入,数据才算写入成功,可以保证强一致性。

使用同步复制、同步刷盘,消息延迟一定会比不使用此模式更大。

RocketMQ为了提高写入性能,在内核层提供了读写分离的机制,引入了transientStorePoolEnable。默认情况下,消息会先进入PageCache,再通过同步/异步刷盘进入磁盘文件。而transientStorePoolEnable=true 时,消息会先进入堆外内存,然后通过FileChannel块提交的方式批量提交到FileChannel,再通过异步刷盘批量进入磁盘文件。

堆外内存技术可以保证数据常驻内存中,不会因为内存紧张而将数据交换到其他内存,做出这种方式,能够提高更高的写入性。同时,写入流程没有经过PageCache,但依然从PageCache读取,在内核层实现了读写分离的方式。其优点为性能较高,投入资源较少。缺点是容易丢消息,因为存在堆外内存中的消息可能会出现没有批量提交到磁盘的情况,从而造成消息丢失。

性能与数据丢失如何权衡?

我们认为,性能要追求(资源投入少),数据库正确性也要保证。

首先考虑数据发生丢失的概率以及数据找回的成本是否可控,如果数据找回难度低,则毫不犹豫地选择性能优先。比如,数据存储在binlog中,一般保存 15 天。则该情况下集群可以使用异步复制和异步刷盘。并推荐开启transientStorePoolEnable=true。即使集群服务节点异常导致机器断电等情况造成消息丢失,依然可以通过消息回溯找到断电前的数据。且发生此类情况的概率较低,没有必要为低概率事件牺牲性能。

此外,应避免人为因素(集群运维)导致数据丢失,进一步降低人工介入次数。比如开启transientStorePoolEnable=true,堆外内存重启时导致数据丢失。发生上述情况时,可通过以下方案保证内存不丢失:

①关闭一组broker的写权限,只保留其读权限,即原先存在的消息依然可以消费。

②待broker写入TPS为0后,停止broker。

③运维操作结束后,启动broker。

④开启broker写权限。

消息发送者或消费者查询信息时,不会访问关闭了写权限的broker。比如有四台broker,每个broker有四个队列,关闭其中一台的写权限后,返回的只剩 12 个队列。通过此方式,可以平缓地将流量停止。流量停止后,堆外内存中的数据必然会被刷到磁盘中,以此保证数据不丢失。

RocketMQ支持分区级别的顺序消费,以银行账户余额变更短信通知为例:

发送方按照key (银行账号)进行哈希取模,取完后变为q0、q1、q2、q3。然后保证同一 key 的消息能进入到同以队列,消费者使用顺序消费的模式能够保证单个分区中的消息按顺序依次进入。

RocketMQ主要通过锁一致来实现顺序消费。消费者在拉消息前,会先在broker服务器锁定队列,锁定成功则可以进行消费;否则不会消费,等待下一次消息队列。

消息进入pullRequest队列后,消费者首先会在本地锁定队列,比如消费者分到 q0 和q1,则会先申请 q0 的锁再进行消费以及申请 q1 的锁再进行消费。

上述流程中,RocketMQ只支持分区级的并发度。比如消费者被分配了 30 个线程,实际只能有两个线程同时工作。该策略会导致如果消息队列有积压,调整消费者线程数没有任何效果。

破解并发度困境的关键词为:关联顺序性。

关联顺序性指同一个队列中不同账号的消息并发执行,同一队列中相同账号的消息串行执行。

如上图,以账户余额短信通知服务为例,q0队列中有1、3、5、3、9,只需要保证其中的1、3、5、9并行执行,而前后两个3按顺序执行即可。

上图为顺序消费模型的优化方案。

定义一个线程池,消费时按哈希取模,使同 key 的消息进入同一线程,不同key的消息分散在所有线程池中。比如原先有10个线程,不够用则可增加至20个线程,即破解了并发度带来的困境。该模型下,并发度与队列无关,可任意根据需求设置并生效。且实现了无锁化设计,按 key 选择线程。

RocketMQ4.6.0版本提供了DefaultLitePullConsumer API,其功能与Kafka高度类似,实现了RocketMQ与Kafka的通用性。

全链路压测的基本设计需求有两个点:

①隔离性:如何存储压测流量,使其与正式流量互不影响。

②上下文信息:链路中一部分接入而另一部分不接入的情况下,上下文信息如何存储?

我们主要通过影子topic和影子消费组实现了全链路压测方案。

如上图所示,消息发送到中通自研数据同步平台后,会判断其是否为压测数据。如果是,则发送至shadow_T_ORDER_TOPIC,否则发送至T_ORDER_TOPIC。

order_consumer中包含shadow_C_ORDER_CONSUMER和ORDER_CONSUMER,分别消费压测消息和非压测消息。没有接入全链路压测的消费者指消费非压测数据。通过以上方式,从消息发送和消息消费链路上实现了将流量分开。

同时,其他相关信息比如 ID 等会存储于RocketMQ的消息属性中。

未接入全链路压测的应用无法识别消息属性,因此也无法区分消息是否带有压测属性,会导致流量全部打到不接全链路压测的ORDER_CONSUMER,因此不适合用消息属性进行隔离。如果希望使用消息属性进行隔离,则数据必须全部是业务方会消费的消息。

03 中通基于RocketMQ平台化建设实践

中通基于RocketMQ的平台已经开源,开源地址如下:

https://github.com/ZTO-Express/zms

在中通,目前生产环境中所有的kafka集群、RocketMQ集群、Zookeeper等就能可以直接通过页面操作的方式快速搭建一套集群。实现原理如下:

在页面上访问zms-portal来启动和停止服务进程,由zms-agent启动 supervisor 进程管理体系来启动和停止服务。安装过程中,将参数提供给zms-agent,再发送给supervisor启动脚本,以启动服务。

今年,中通计划实现关于MQ集群的容灾恢复策略,扩容、主题迁移,在平台上一键操作完成消费组迁移而无需执行其他运维命令等能力。

NameServer地址动态感知机制指:项目组使用ams-sdk进行消息发送,消息消费时无需感知nameserver地址,只需面对topic、消费组编程;若集群出现问题,可以无感知地实现topic和消费组从一个集群迁移至另外一个集群。

NameServer地址动态感知机制的实现原理如下:引用 ZK 存储元信息,zms-portal在新增、修改、删除时,会去操作Message集群,同时会将操作写入到 ZK,在 ZK 中存储 topic 属于哪个集群、nameserver 地址等。zms-client发送消息之前,会查询topic的元信息并根据元信息构建底层的发送者进行消息发送。

如果要从一个集群迁移到另一集群,可以先修改元信息并更新ZK,ZK 更新后,zms-client会订阅 topic 内容的变化,如果发生变化则通知 SDK 重新构建发送者,实现切换。

中通实现了对集群、主题、消费组等的可视化监控与告警体系。此能力的实现主要通过zmsCollector服务监听ZMS节点的变更并返回集群数据,由Message Cluster收集集群指标、订阅客户端指标数据给zmsCollector,再存储至influxDB最终进行展示。

此外,RocketMQ 的客户端耗时等指标,我们也在zms-sdk那进行埋点,发送至Message Cluster后,由zmsCollector进行消费,然后发送给influxDB最终进行展示。

加入 Apache RocketMQ 社区

十年铸剑,Apache RocketMQ 的成长离不开全球接近 500 位开发者的积极参与贡献,相信在下个版本你就是 Apache RocketMQ 的贡献者,在社区不仅可以结识社区大牛,提升技术水平,也可以提升个人影响力,促进自身成长。

社区 5.0 版本正在进行着如火如荼的开发,另外还有接近 30 个 SIG(兴趣小组)等你加入,欢迎立志打造世界级分布式系统的同学加入社区,添加社区开发者微信:rocketmq666 即可进群,参与贡献,打造下一代消息、事件、流融合处理平台。

微信扫码添加小火箭进群

另外还可以加入钉钉群与 RocketMQ 爱好者一起广泛讨论:

钉钉扫码加群

RocketMQ 在物流行业的应用与运维的更多相关文章

  1. 一切从“简”,解放IT运维人员

    运维人的神技 运维既是个技术活儿也是个苦差事,而运维人员被期望有着无限的技能:主机.存储.网络.操作系统样样精通,而且还要会写SQL.shell.开发语言java..net.python等等,对业务更 ...

  2. IT Operations(IT 运营),运维的更价值化认识

    一直想努力向别人(甚至包括从事运维的人)解释清楚什么是运维,发现很难! 6月20号,在InfoQ高效运维群里面,对运维创业做了一次激烈的讨论,很自然地,过程中不可避免的谈到运维苦逼和运维无法产品化的问 ...

  3. 运维人员:走好你的IT运维路

      转自 http://os.51cto.com/art/201303/387120.htm   现阶段,大多数运维人员只是处于被动低效率手工救火的状态,企业对其重视程度不高,导致部分运维人员对自己的 ...

  4. (转)医疗IT运维系统

    http://www.ewei.com/ask/87.html 含义解释 itil运维管理系统,为用户提供专业的it运维管理,对网络运行的状态.故障.性能等监控,又从业务的视角为管理人员提供综合分析和 ...

  5. 王磊:AI 时代物流行业的 OCR 应用

    欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ OCR 是人工智能里面非常重要的基础能力之一.腾讯云人工智能产品总监王磊,结合物流场景解读了OCR技术."OCR文本识别能够优化 ...

  6. 女生可不可以进入IT行业做Linux运维工程师?

    不知从何时起有那么一个不成文的理论:女生不适合做IT.在很多人看来,IT is a men’s world,女生学IT是件匪夷所思的事情.在传统的思维当中,女生只适合从事像教师.会计.公务员等稳定的职 ...

  7. 2019年,Linux运维行业的趋势,跟不上学习就被淘汰

    运维行业经历了多年的发展,已经有了很大的变化,最开始的机房.网线.人肉,到现在一步步的自动化.智能化.容器化,运维人员的职业技能要求越来越高,稍不注意就可能被淘汰. 今天马小哥就来盘点一下2019年运 ...

  8. rocketmq运维管理

    # 运维管理--- ### 1 集群搭建 #### 1.1 单Master模式 这种方式风险较大,一旦Broker重启或者宕机时,会导致整个服务不可用.不建议线上环境使用,可以用于本地测试. #### ...

  9. RocketMQ系列:rocketmq运维控制台使用详解(全网独家)

    搭建好了RocketMQ的console之后,直接在浏览器打开http://{你的ip}:8080 默认会进入到驾驶舱(dashboard). 总览 整体横向菜单分为八个部分: 运维:主要是设置nam ...

  10. 什么是业务运维,企业如何实现互联网+业务与IT的融合

    业务运维并不是一个新概念,针对传统信息架构提出的业务服务管理就是把以业务为核心的IT系统与IT基础设施性能进行整合运维的解决方案.然而随着互联网+转型的不断推进,基础设施的智能化和广泛云化成为IT发展 ...

随机推荐

  1. Python条件语句的用法

    python条件语句使用 if 表达式,难度不高,需要注意的是嵌套用法,以及如何设置对应的条件. if 条件判断语句 python 语句是按固定顺序执行的,先执行前面的语句,再执行后面的语句.如果你像 ...

  2. python包合集-argparse

    一.argparse简介 argparse 是 python 自带的命令行参数解析包,可以用来方便的服务命令行参数,使用之前需要先导入包 import argparse 二.简单案例 简单使用,创建一 ...

  3. 《吐血整理》高级系列教程-吃透Fiddler抓包教程(21)-如何使用Fiddler生成Jmeter脚本-上篇

    1.简介 通过跟随宏哥的脚步学习宏哥的Jmeter系列文章,.我们知道Jmeter本身可以录制脚本,也可以通过BadBoy,BlazeMeter等工具进行录制,其实Fiddler也可以录制Jmter脚 ...

  4. HCIA-STP原理与配置

    STP协议生成树协议: 为了保证网络可靠,所以在组网时需要设置冗余链路和设备,从而在物理结构上形成结构,又因为交换机的工作特点导致二层网络中产生广播风暴和MAC地址表震荡现象,影响用户体验. 广播风暴 ...

  5. Kubernetes Operator: CRD

    Custom Resource Define 简称 CRD,是 Kubernetes(v1.7+)为提高可扩展性,让开发者去自定义资源的一种方式.CRD 资源可以动态注册到集群中,注册完毕后,用户可以 ...

  6. 重要参考步骤---ProxySQL Cluster 集群搭建步骤

    环境 proxysql-1:192.168.20.202 proxysql-2:192.168.20.203 均采用yum方式安装 # cat <<EOF | tee /etc/yum.r ...

  7. CentOS无法识别NTFS格式U盘完美解决方案

    问题描述:CentOS上无法识别NTFS格式的U盘 解决方案: # 进入yum目录 cd /etc/yum.repos.d # 下载阿里的epel wget http://mirrors.aliyun ...

  8. 解决zeal离线文档下载慢问题

    zeal简介 编程过程中难免会遇到不会用的关键字和方法,对我而言,在windows下,我使用Zeal这个软件进行离线文档查询. 问题 但是,在软件中下载DocSet(文档)会出现下载慢,或者下载不了的 ...

  9. Docker搭建自己的Gitlab CI Runner

    转载自:https://cloud.tencent.com/developer/article/1010595 1.Gitlab CI介绍 CI:持续集成,我们通常使用CI来做一些自动化工作,比如程序 ...

  10. MySql的InnoDB的三层B+树可以存储两千万左右条数据的计算逻辑

    总结/朱季谦 B+树是一种在非叶子节点存放排序好的索引而在叶子节点存放数据的数据结构,值得注意的是,在叶子节点中,存储的并非只是一行表数据,而是以页为单位存储,一个页可以包含多行表记录.非叶子节点存放 ...