京东架构师闫文广:订单系统高可用架构及演变过程
闫文广
京东到家后台研发部架构师
读完需要
20分钟速读仅需 7 分钟
从事支付系统、计费系统和订单履约系统等后台领域的研发,现专注于订单中心架构优化和研发相关的工作。本文根据闫文广老师在〖deeplus 直播第 242 期〗线上分享演讲内容整理而成。(文末有获取本期 PPT&回放的方式,不要错过)
大家好,我是京东到家后台研发部的架构师闫文广,今天将给大家分享京东到家订单系统的高可用架构及演变过程。
京东到家是达达集团旗下中国最大的本地即时零售平台之一,目标就是实现一个小时配送到家的业务。一直到 2019 年京东到家覆盖 700 个县区市,合作门店近 10 万家,服务数千万消费者。随着订单量的增长、业务复杂度的提升,订单系统也在不断演变进化,从早期一个订单业务模块到现在分布式可扩展的高并发、高性能、高可用订单系统。整个发展过程中,订单系统经历了几个明显的阶段,通过不同的技术优化方案解决业务上遇到的问题。
下面我将为大家逐一介绍我们遇到了哪些问题及如何解决,主要分为以下三部分:
京东到家系统架构
订单系统架构演进
订单系统稳定性保障实践
1
京东到家系统架构
1.1
业务架构
首先来看以下这张流程图,这个系统架构主要由几个部分构成:用户端分别是 C 端用户和 B 端用户。B 端用户针对的是像沃尔玛、永辉超市等的一些商家,商家生产需要用到我们的一些拣货 APP 和拣货助手,后面商家履约完成会用到配送端,配送端就是给骑手接单抢单,最后是结算部分,分别给骑手和商家结算。
1.2
运营支撑业务架构
京东到家的运营支撑业务架构主要分为商家管理、CMS 管理、营销管理、财务管理、运营数据这五大模块,每块包含的内容具体如下图所示:
1.3
后端架构
业务支撑包含了很多业务系统,比如用户、定位、地址库、运费、promise、推荐、搜索、收银台、风控等。
上面说到了营销的一些管理系统,其实营销还有一些后端的基础服务系统,比如优惠券、满减、秒杀、首单等。
1.4
订单数据入库流程
用户提单以后数据怎么流转?提单其实是一个把用户下单数据存储到数据库,提单系统做了一些分库分表。那么提完单的数据怎么下发到订单系统生产?首先我们会有一个管道,提单通过一个分布式异步任务来下发订单管道里。所有的订单下来都会放到管道里,我们通过一个异步的任务,按照一定的速率,均匀地把订单下发到订单生产系统。这样设计有一个好处,比如像大促时可能会有大量数据一下子下发到订单生产系统,对订单生产库有很大压力,所以我们中间设计出一个管道,通过异步任务来分发生产订单。
那么订单系统跟个人中心是怎么交互的?首先异步,我们是通过 MQ 来交互这些订单状态的变更。另外 C 端的订单取消,是怎么同步到订单生产系统的?我们是通过 RPC 的调用来保证订单实时取消,有一个返回结果。
2
订单系统架构演进
2.1
订单系统业务流程
我们的订单履约分为两大块,商家生产和配送履约,具体步骤如下图所示:
在商家完成拣货时,我们订单会分为分区拣货、合单拣货、前置仓拣货这几块业务上的操作,其实在系统里我们有一个拣货的池子,会通过不同维度的数据来完成高效的拣货。拣货完成后,我们的配送主要分为两个模块,一种是单个订单的配送,另一种是集合单的配送。集合单就是把发单地址和配送地址在两个相近的格子里的订单合并起来,基本上都是基于将同一个门店的配送目的是同一个相近格子里的订单,进行合单后让一个骑士完成配送。配送会下发到一些配送系统,分为两种模式,集合单和单个订单的配送,以及和配送系统的整个运单交互的一个流程。
2.2
RPC 微服务化集群
说完业务后,接下来介绍一下我们应用的一些微服务的拆分过程。先讲一下微服务理论方面的知识,比如为什么要拆分微服务?微服务拆分后可以解决哪些问题?这是接下来一个重点内容,大家可以思考一下,系统架构必须具备哪些条件才能达到高可用?
简单总结来说,微服务可以降低系统的复杂度,可以独立部署,并且有很好的扩展性:
降低复杂度:把原来耦合在一起的业务,按领域拆分为不同的微服务,拆分原有的复杂逻辑,每个微服务专注于单一业务逻辑。明确定义领域职责与边界;
独立部署:由于微服务具备独立部署运行能力,当业务发生变更升级时,微服务可以单独开发、测试、部署升级。提高了迭代效率,降低了研发风险;
扩展性:拆分后的微服务架构独立部署,可以根据流量预估或压测评估独立进行扩容升级。
Redis 的接入解决了什么问题?数据库为什么要分库?ES 为什么在接下来一些系统架构升级里会被引入进来?为什么 DB 要拆成多个集群?这背后的一些根本问题,以及解决业务系统的一些背景,接下来我们逐一探讨。
按微服务拆分成多个系统,如果发布有问题也只会影响其中的一些很小的部分。在后面随着业务量越来越大,RPC 这种框架的引入,解决故障的自动下线,保证高可用,比如单台服务器有问题时,能做到自动下线来保证不影响业务。
2.3
订单系统架构升级 - Redis 集群
我们数据库为什么升级?因为数据库的数据量越来越大,比如添加一些字段,它其实会做一些锁表操作,随着数据量越大,单表的数据越来越多,数据主从延迟以及一些锁表的时间会越来越长,所以在加字段的时候对生产影响特别大,我们就会对数据做一个分离,把一些冷的数据单独做一个历史库,剩下的生产库只留最近几天的一些生产需要的数据,这样生产库的订单数据量就会很小,每次修改表的时间是可控的,所以我们会把数据按照冷备进行拆分。
至于为什么引入 ES,是因为订单在生产方面会有一些很复杂的查询,复杂查询对数据库的性能影响非常大,引入 ES 就可以很好地解决这个问题。
另外,引入 Redis 双集群,Redis 其实有一些大 key 的问题,当一些核心业务和非核心业务都用到 Redis 的时候,我们会把一些核心业务拆到一个集群,非核心业务拆到另一个集群,来保证 Redis 集群的稳定性,能稳定支撑订单生产。
注:大 key(线上看到过 list 的 elements 超过百万的)删除时会阻塞比较长的时间,大 key 的危害:
OPS 低也会导致流量大,比如一次取走 100K 的数据,当 OPS 为 1000 时,就会产生 100M/s 的流量;
如果为 list,hash 等数据结构,大量的 elements 需要多次遍历,多次系统调用拷贝数据消耗时间;
主动删除、被动过期删除、数据迁移等,由于处理这一个 key 时间长,而发生阻塞。
2.4
订单系统数据架构 - MySQL
2.5
订单系统架构升级 - ES 集群
为了解决这个问题,我们引入了 ES 的冷备两个集群,热集群只保存跟数据库一样的生产库的数据,比如说我们现在保证的就是 5 天的生产数据,其它所有数据都归档在一个 ES 的冷集群里。通过这种异步跟同步写,通过异步任务来保证最终的集群的数据一致性。这就是 ES 的架构升级。
3
订单系统稳定性保障实践
大家思考一下,如果你要负责一些核心系统,你怎么保证稳定性?接下来我会从订单系统可用性建设、系统容灾能力、系统容量能力、系统预警能力分享一下我们在稳定性保障上的实践。
3.1
订单系统可用性建设
业务的快速发展对可用性保证要求越来越高,在方法论层面,我们按照系统故障的时间顺序提出了事前、事中、事后三个阶段,同时提出了四方面的能力建设——预防能力、诊断能力、解决能力、规避能力。
3.2
系统容灾能力
另一个维度来看,我们也会先灰度单台机器,再到单机房、多机房。当然这个很局限,因为跟你灰度的一些功能有关系,大家要酌情根据自己的业务借鉴这种方案思路。
3.3
系统容量能力
3.4
系统预警能力
在接口层面,我们可以监控到:
可用率;
响应时间;
调用量:当别人调用你的接口,你设置调用的一个量值,当超过阀值时就是进来了一些非正常的流量,当监控到这些异常流量,就可以做限流等相应操作;
自定义:自定义一些报警;
关键词:当系统出现某种问题,需要关键字报出来然后进行人工介入;
调用链:一个接口操作下来,谁调用了你?你调用了谁?哪个环节有问题?
在应用层面,我们可以监控到:
Young GC;
Full GC;
堆内存;
非堆内存;
CPU 使用率;
线程数。
CPU 使用率;
系统负载;
内存;
线程数;
读写 IO;
磁盘使用率;
TCP 连接数。
如果大家有排查过线上的问题,就应该能感受到比如像 IO 高、TCP 连接、重传等,都会影响到线上一些核心接口的响应时间,包括你 CPU 高的时候,线程数是否飙高?系统负载是否飙高?当这些指标都发生异常变化时,对于接口的响应时间都会有很大影响。
另外,我们还要做一些积压监控,比如一些异步任务正常来说一分钟最多积压 1000,就需要添加对应的监控,否则数据异常了都不知道;再比如订单支付的状态,当积压到一定数量,可能是系统出了问题,就需要人工介入。
4
总结
一个企业的架构师团队,需要长期关注技术驱动业务、明确领域职责与边界等关键问题,同时架构的演进过程也是不断考虑 ROI 的权衡取舍过程。技术的持续发展,有助于不断提升用户体验、业务规模,降低运营成本,而架构在其中需要解决的问题就是化繁为简,将复杂问题拆解为简单的问题逐个攻破。随着企业规模的持续增长、业务的持续创新,会给系统架构提出越来越高的要求,所以系统架构设计将是我们长期研究的课题。在这条架构演进之路上,希望能与大家共勉共进。
Q&A
Q1:集群规模大概是什么样的?各集群节点规模如何?
A:京东到家订单中心ES 集群目前大约有将近30亿文档数,数据大小约1.3TB,集群结构是8个主分片,每个主分片有两个副本,共24个分片。每个机器上分布1-2个分片,如果企业不差钱最好的状态就是每个分片独占一台机器。这些集群规模和架构设计不应该是固定的,每一个业务系统应该根据自身实际业务去规划设计。
这样做确定分片数:
ES是静态分片,一旦分片数在创建索引时确定那么后继不能修改;
数据量在亿级别,8或者16分片够用,分片数最好是2的n次方;
如果后继数据量的增长超过创建索引的预期,那么需要创建新索引并重灌数据;
创建mapping是请自行制定分片数,否则创建的索引的分片数是ES的默认值。这其实并不符合需求;
副本数:一般设置为1,特色要求除外。
Q2:ES 优化做过哪些措施?
A:ES使用最佳实践:写入的数据考虑清楚是否会过期,ES擅长的不是存储而是搜索,所以一般存入ES的数据难免会随着时间删除旧数据。删除方法有两种:①按记录(不推荐)②按索引。推荐使用后者,所以需要业务根据数据特点,按天、月、季度等创建索引。分片数够用就好,不要过多不要过少。ES不是数据库,不建议做频繁的更新。
Q3:集群可承受的 TPS 是多少?
A:这个没有具体的数字,根据不同规模集群、不同的索引结构等不同,建议根据业务评估自己的流量,压测双倍流量,若ES无法承受或满足,可以考虑扩容集群,不要流量暴增于平时的3倍、4倍,甚至更多的规模。
Q4:ES 主要是用于明细单查询,还是聚合统计?Join 对资源耗用大吗?如何控制内存及优化?
A:ES在订单系统中的实践主要是解决复杂查询的问题,ES不建议使用聚合统计,如果非要使用那我也拦不住你,哈哈哈。
深分页的问题【内存】
ES处理查询的流程如下:
Client需要第N到N+m条结果;
接到这个请求的ES server(后继称之为协调者)向每一个数据分片所在的数据节点发送请求;
每一个数据节点都需要向协调者返回(N+m)个结果;
如果有n个数据分片,那么协调者拿到n * (N+m)个结果,排序,扔掉(n-1) * (N+m)个结果,返回给client N+m个结果;
如果N是10W,100W,那么协调者的内存压力会非常大;
在我们目前维护的2.1版本中,ES已经不容许N>10000了。
深分页的危害:导致打爆节点内存引起集群整体不可用。
Q5:应用 canal 同步数据,会有延迟吗?
A:首先来说下ES 特点:Elasticsearch是一个接近实时的搜索平台。这意味着,从索引一个文档直到这个文档能够被搜索到有一个轻微的延迟(通常是1秒),这个参数也是可以调整的,根据业务场景调整。
可以肯定的是延迟是肯定的。其实延迟大小完全取决你整个同步流程中是否有瓶颈点,如果业务要求实时的,其实不建议在这种场景下使用ES。就好比数据库查询从库不能接受延迟,那就不要做读写分离,或者都查询主库。
Q6:sqlproxy 具体用的哪个?
A:sqlproxy这个是指MySQL读写分离的实现,大家可以网上查询下有很多资料。官网地址:https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-master-slave-replication-connection.html
<mvn.jdbc.driver>com.mysql.jdbc.ReplicationDriver</mvn.jdbc.driver>
<mvn.jdbc.url>jdbc:mysql:replication://m.xxx.com:3306,s.xxx.com:3306/dbName</mvn.jdbc.url>
Q7:Redis 用于查询缓存、分发任务缓存?
A:Redis在项目中的使用场景,缓存查询,分布式锁使用。其中还有一个异步任务是通过redis zset + tbschedule 定时或实时的去执行一些业务逻辑。
添加队列数据方法:
public Boolean zAdd(String key, final double score, String value) ;
查询获取队列数据:
public Set<String> zRangeByScore(String key, final double min, final double max) ;
Q8:容量评估可以讲一些细节嘛?
A:基本有两种场景:
日常业务流程是否有瓶颈 ;
大促期间根据流量预估系统是否有瓶颈。
京东到家内部系统是有一套完整的监控系统,基于接口,应用机器,集群的多维度监控。
接口:
响应时间,tp99,tp999,tp9999 等;
接口调用量,次数/分钟;
可用率。
应用机器
根据监控可以查看单机器相关指标数据是否正常,比如:
CPU使用率;
系统负载;
网络IO;
TCP连接数,线程数;
磁盘使用率。
集群
Redis集群;
ES集群;
MySQL集群。
对于集群来说是根据集群下机器指标是否正常来评估整个集群是否正常。需要看集群可以承载业务流量的TPS、QPS等指标是否满足业务需求,同时需要评估大促场景下是否可以满足要求。这种情况就需要根据大促流量评估压测,看集群以及应用,接口是否可以满足需求。
每个公司可以根据自身规则进行扩容,及架构升级。比如日常CPU超过60%考虑应用扩容,负载远大于机器核数等等。
Q9:异步定时任务用的是什么中间件?
A:tbschedule是一个支持分布式的调度框架,让批量任务或者不断变化的任务能够被动态的分配到多个主机的JVM中, 在不同的线程组中并行执行,所有的任务能够被不重复,不遗漏的快速处理。基于ZooKeeper的纯Java实现,由Alibaba开源。
Q10:在云上部署还是物理服务器?
A:应用都部署在云服务器上,首先即时,几分钟即可完成,可一键部署、也可自主安装操作系统。安全性方面因为服务分布在多台服务器、甚至多个机房,所以不容易彻底宕机,抗灾容错能力强,可以保证长时间在线。弹性以及可扩展性方面云主机基本特点就是分布式架构,所以可以轻而易举地增加服务器,成倍扩展服务能力。
Q11:RPC 高可用怎么实现?
A:RPC高可用基本都是借助于分布式框架,阿里开源dubbo,Spring全家桶的SpringCloud,包括我们使用的京东自研的JSF。其工作原理,感兴趣的同学可以网上搜下,很多资料。在这儿就不一一解答了。
- EOF -
END
#架构师必备#
↓点这里可回看本期直播