导读:对于应对突发的峰值访问每个技术团队都有自己的经验及方法,但是这些方法远没有得到体系化的讨论高可用架构在 6 月 25 日举办了『高压下的架构演进』专题活動,进行了闭门私董会研讨及对外开放的四个专题的演讲期望能促进业界对应对峰值的方法及工具的讨论,本文是杨超介绍京东账号有陌生人订单交易系统如何应对高压的实践
杨超,京东账号有陌生人订单商城架构师2011 年 10 月加入京东账号有陌生人订单。先后负责和参与京东账号有陌生人订单的 IM 项目、交易系统 .NET 转 Java、购物车、库存、多中心交易等核心系统的研发和架构升级工作
大家好!我是来自京东账号囿陌生人订单商城交易平台的杨超,今天特别高兴能够来给大家分享每年 618 及双十一所做过的工作我是 2011 年加入京东账号有陌生人订单,在這 5 年中我经历了不少技术演进也看到了不少变化,在这里给大家做一个分享
先介绍一下交易系统基本情况。
这张图是整个京东账号有陌生人订单商城数据流向结构这个图主要分为三个部分。
-
订单生成前包括单品页,购物车架构,促销等功能我们每个用户进来需偠访问。它的特点是大促期间访问量非常大后面会详细介绍如何应对。
-
订单预处理订单生成之后,这是一个原始生成单之后需要对訂单进行预处理,进行拆包包裹的拆分、大家电小家电拆开包裹运送等。因为是统一下单这一块是订单预处理。挑战是访问量大各個模块如拆单、订单转移,支付台帐等可能会承受非常大的压力我们会采取扩容存储,限流数据结构优化等方法去应对。
-
订单履约阶段真正到后面是整个处理过程,配送黏合起来做的系统这是京东账号有陌生人订单商城的服务结构。
下面是交易结构图从左到右,單品页是网站平台做的今天来到现场的也有一些参与过的同事。移动端、微信、手Q等入口都会调到购物车服务。
从购物车开始我们從上到下是一个典型的分层结构,上面是调用来源中间是我们的服务,下面是依赖的底层服务其中强依赖服务是关键路径所需要调用嘚服务,是主流程中不可缺少的一部分
强依赖服务在大促期间不能被降级,我们需要提前扩容以及进行代码重构、拆分、按来源单独蔀署等方法提前进行优化。
为了更好应对我们需要对用户访问的特点进行分析。参看下面图2011 年到 2015 年整个的单量,2011 年618 是几十万的单量,去年单量一天是几千万看着单量往上增长的这张图,就能感受到系统压力有多大
为了应对大促的压力,我们必须清楚知道用户访问系统功能的流量及分布经过数据统计,接单前面这波系统正常每一单,有一个前几年的大概的统计购物车:结算页:产生订单页面訪问的比例是 16 :4 :1,也就是说购物车访问 16 次结算页访问 4 次,提交订单页面访问 1 次到 618 及双十一,每天 PV 就是几十亿几百亿、上千亿,因此我看到最大的量 1
分钟是几千万我需要清楚知道这几千访问落到那几个页面。
根据京东账号有陌生人订单传统每年定一个目标,618 当天戓者三天需要达到多少亿的指标比如说一百亿或者几百亿,后面我们会把钱换算成我们的单量我们客单价是多少?如果客单价是 300目標要 100 亿,则我们单量需要达到百万或者三千万这样通过预估出来当天的单量会有多少,这是提前的准备的整体规划过程
为了每年的 618、雙十一系统的稳定,京东账号有陌生人订单研发如何应对
系统底层的调用量是知道的,往年的 618 或者往年的双十一也可以找到经过半年嘚业务跟进,我们系统会有很多的变更数据变更或者是代码变更结构变更都会产生,我们知道这个系统能够承受多大量上来对它进行壓测。
压测分为线上压测、线下压测主力做线上压测。
为什么我们会采用线上压测早年我们只做线下压测,环境跟线上不一样路由器和机器 CPU,物理机每一个不相同或者架设的路由超过 3 层,丢包各种数据不一样,压测出来的数据经常会差异
线上压测分开是怎么样莋的?需要将读业务跟写业务区分开读业务,我们正常可以看到读价格读库存、读购物车场景的分开读跟写,看到购物车上的分布僦能知道是读还是写。
从压测上在集群中将服务器缩减,因为我们支撑的量最高量达到 1 分钟达到 1 亿左右,平常最少有几十万、几百万嘚量集群肯定是比较大的,最少也是几十台的机器我们会把集群机器逐台往下缩减,真正看到线上量能扛到什么情况
做到这儿,大镓会有疑问风险挺大。对风险的确挺大,比如一个集群的 30 台机器一个一个往下缩比如缩到 5 台,如果扛不住所有的机器就崩溃,就會面临很大风险所以梳理完每个架构之后,每年我们冒着风险找到这个点,往上一点的量进行缩减缩到一定程度再强行缩。
主要通過 TCPCopy 复制端口流量多层翻倍放大流量。如下图就直接将每层流量翻倍整体就是 1,000 倍工具实现简单,可以实现多条线组合进行流量复制通過这种方式发起超负荷的请求,检验服务能够承载的容量
我们做了一个成立了一个压力小组,线上压力测试小组我们做线上压测。用非常简单的底层工具去做压测底层发起的量特别快而且特别多,集群我们只做了压测平台,把这些工具集成起来做模拟流量压测
在數据模拟上,我们是自己事先会准备一批数据比如说几万个用户,几万个 SKU商家各种库存模式,促销模式在模拟平台我们会准备好。
峩们把订单在这个结构接住堵在这个地方不往下放,往后拽都是密集的一些服务从这一块把量堵住,堵几十万突然有一天打开,看箌一个峰值看每一分钟处理量,往后能承受多大量是不是能够承受发起的量,
大家可能在朋友圈看到照片各个服务的核心人员,集Φ在一个会议室进行压测。一步一步往上加量严密监控线上响应情况、订单量情况、各个服务器,以及各个缓存、数据库等机器的实際负载情况出现任何风吹草动就停止发起压力,并进行记录和排查问题
然后压测订单提交,往主集群写数据跟购物车不同,这种压測会直接在生成集群上进行压测并会写入数据。因此需要将写入数据进行隔离操作并将垃圾数据进行数据删除,不能进入生产环境
根据业务和技术维度筛选一批商品、一批用户,主要覆盖存储分布、用户每个等级以及业务分支促销组帮忙建立能覆盖所有环节的促销數据。将这些用户的提交订单后清空购物车的功能禁用保证能不停的重复下单。另外这些用户的订单提交流程中的邮件、短信提醒等相關功能禁用产生的订单进行隔离,不往生产系统下发并在测试完成后进行删除。
线上压测时组织各个相关组核心人员严密监控各项數据。出现问题立即停止压测先进行恢复,同时进行数据记录和问题排查如分钟级无法恢复则直接切亦庄备用集群。
每个服务分别进荇一轮压测记录每个服务和购物车、订单提交压测得出的数据。根据线上实际用户调用比例进行换算得出一个相对精准的整体集群承載数据。
订单生产后系统主要用憋单,快速释放流量进行压测形成对整个后续系统的,持续性高流量冲击得出整体系统的处理订单能力。
下面是压测的 DPMP 系统结构图
通过压测,就知道目前京东账号有陌生人订单系统压测完能承受多大量,面临我们目标差距有多少壓测完之后,我们就要运维优化
在打压时候,我们按照交易系统的流量分布来模拟流量比如正常访问购物车与结算页是 16 :4 的流量,下圖的在打压时候我们也严格按照这个流量来执行确保压力接近大促时候的真实访问场景。
缓存从前面比较多CDN、Nginx、Java 都会有缓存。
缓存是逐级往下做是一个漏斗状,最开始做缓存到缓存的持续性在很短的时间内,一分钟或者一秒钟或者毫秒这样给用户的感知是看不到緩存的,如果你要承载这么大量必须逐级做缓存,前面做一些静态缓存掉后面会做一些基础数据缓存,最后大数据一层一层往上能擋住整个这一块,
这是购物车大概的结构这里有一个异步双写,我们会写丢这个数据写丢没关系,我们购物车是整体的加一个商品,写不过来下次过来又会全覆盖。这样购物车就有一个多机房多活的可用性
调优三:超热数据的缓存
购物车里面做热数据缓存,这种數据的缓存比如促销服务直接影响到价格,缓存效率必须是在秒级毫秒级在一秒钟怎么筛选十亿商品里面最热的商品?
我们利用 Queue 的原悝不断往里塞 SKU,队列的长只有 50传进来之后,这里有的位置往前移我们很快知道在一秒钟知道,排在前面肯定是访问次数最多的每┅个阶段应用存储访问最多的数据,如果是秒杀商品500 万的请求有十万到二十万,它肯定大部分的请求在这块就出去了不会穿透进来,這是我们自己做的热数据缓存
对 Redis 存储的数据进行压缩,这样空间又缩小四分之一或是三分之一我们数据到后面就会很小。当量小之后访问效率就会升高,你数据量弹出很小丢单率很小,可以提高我们的可用性
什么是异步?购物车会调三个服务如果是串行,一个 2 毫秒累计起来是 6 毫秒。同时去处理同时往后并行,这三个服务同时去调根据业务规则要先调商品,再调促销调两次,肯定在三毫秒之内
异步数据落地,中间存储来解决访问流量过大冲击原始存储的问题。包括库存状态库存数据的剥离库存状态调用量太大,穿透直接到库存数据
异步异构用得最多在这个系统里面。这一步接单系统的异构接单系统在这一块的异构。我们整个接单一次性提交訂单、购物车是提交成一份数据,这样提高效率
如果说按照原来的做法,直接写到表里面有很多订单明细、促销明细、优惠券很多要寫,这样访问效率会存在瓶颈因此后面写到接单服务,再异步调动某一个状态机,通过管道服务再衍生出来拆分成订单中心数据支付台账,异构之后单个系统的应对峰值的能力都得到了提升。
提交订单、接单做了异步处理
再往下订单中心又会有很大异构,分成了 4 個子系统去分别调用订单中心会产生列表服务数据,列表服务数据根据PIN的维度用户维度看到数据存储第一步直接写,写不成功就是状態机异步写到这一块存储。
订单中心有一个列表服务的存储再有订单详情的存储,订单详情的存储是根据订单维度去存这一块是根據 PIN 存的。
第三、四部分是单拎出来的状态服务及跟踪服务后续生产跟它直接挂钩。通过异构之后提高了订单中心应对峰值的能力。
后媔看一下看不到商品服务的一切异构ERP 过来,是采销或者 POP 用户进入我们的 MySQL,通过商品发布系统接到商品消息在这一块之前是不能售卖,这一块直接发出去这个消息会写自身的存储,我们会装 A 包、B 包、C包
-
A 包就是基础数据,比如商品名称;
-
C 包比如特殊标识生鲜、易碎。
这样可以把数据分开因为数据量太大了,十个亿的数据就有几百个 G分开几个包。这样性能和可扩展性都得到了提升
商品服务调用方,我只要调订单系统查你,我知道是生鲜、易碎但是前面需要基础数据,商品的名字特殊属性,只有订单结算页需要所以分这麼多的包,我可以分开部署商品服务为什么异构这些包。
发一个通知给别的系统大部分系统依赖基础服务,发一个消息给他商品变哽了,它会缓存自己需要的数据实时计算通过这个地方过来,异构出来存储内部异构出来这么多存储,这是后台用户存储
一个商品垺务,真正做大了可以参考对一个胖的大系统进行拆分的异构方法如果达不到这种规模之前也不建议过细去分。
我们假设系统当超过一萣流量后超过的流量做直接拒绝处理,以便保护后端的服务这就是限流。
Web 的限流根据 PIN 来限流这是根据 IP 加 PIN 风控数据限流,这一块根据業务逻辑一个单一天能下多少单,根据这个逻辑去限流渠道可以按 App、PC、微信等分开,分流和限流这么做
下面讲秒杀系统是怎么来的。秒杀系统是限流和分流的典型
秒杀,假设预约是 1500 万在那一分钟之内,这么多用户过来抢手机也就是单个商品,就把流量直接导到秒杀系统
秒杀系统从 Ngnix 进来就有各种的限制,到我们会识别用户供应商或者商贩去刷的数据这块调用是从正常访问的单品页分出来,不影响主流程
通过 IP、PIN、每一步怎么来、用户以提交记录,一秒钟提交多少次一分钟提交多少次等一堆的规则做判断来限流。到最后再验證有没有预约、常用地址服务等都通过后再调到接单系统。
整个秒杀系统就是一个典型的沙漏的系统当流量跑到后面,实际上只剩很尛的一部分只有真实的写流量到接单。
接单提交服务单独出来两台机器给它用后面的存储得到保护,两台机器最多也就几十万也能承载住,这就是分流跟限流
促销里面也有一个限购,比如前 30 个用户享受促销发一个码出去,需要对这个码进行处理这是一种限流。
促销分流中需要把价格服务单拎出来分出去,单品页搜索手机微信,购物车的架构从这里出来最实时的价格。这样产生分流这一塊有一个存储分流,还有更多其它的就没有一一列举这只是一个示意图。
这就是我们整个的分流跟限流根据前面的渠道,调用量、做哆少程度相对于影响力,做分流和限流
如果分流、限流还没抗住,系统进一步出现压力问题再要做准备做容灾降级。
容灾降级有机房容灾我们做多中心机房,网络容灾、内网外网的容灾应用的容灾,分组、托底容器最后保证基础的服务是正常的。
这是容灾降级这是网络大概示意图。我们的 ISP 进入机房核心交换机、柜顶交换机、这是交换级的容灾,网络共享容灾
购物车结算页的降级,当订单絀现过大延保服务、预约服务如果不行,直接保主流层就属于业务层面的降级。
整个 618、双十一准备下来了后面准备降级方案、容灾方案列出很多,每个业务需要根据自己的情况去考虑上面只是简单列举了几个供大家参考。
最后到临近双十一、618需要网络监控、机器監控、以及了解订单量、登录量、注册量等应用级的监控。
这是 IDC 机房的监控运维部做的。这是单个机器物理机、Docker 的监控。以及对交换機、IP 进行监控这是网络监控。
下面是方法监控每个方法监控到 TP99,99 是最近几次的峰值是达到多少毫秒成功率、失误率、调用次数等。
訂单量的监控一旦出问题都会报警,这是大概的监控系统这是依赖的一些监控系统,我们还会衍生出来自己应用的监控比如说库存,预算是最重要提交订单,保证提交订单成功是最重要从那一堆,库存自己的一套写一个页面写一套监控系统出来,优惠券、购物車写一个小监控系统去监控监控是到大促的眼睛。
一次大促总结下来就这么多,谢谢大家!
Q:您提到做线上的压测会产生很多脏数據,这个数据是最终怎么处理还有一个,您做了好多异构的数据这个数据怎么保持它的一致性。
杨超:先回答你脏数据所谓的脏数據,写数据是为了隔离出来有的是打标,有的是另外起表在数据库里面把这些数据隔阂开来打标,一边写一边删压测,30分钟到一个尛时我们是在凌晨做这些事情,压测完写的数据是非常危险写的数据会爆,引起瘫痪我们实时监控它,打标清理首先切到小集群承载。真实的集群真正的量不会达到618那个量,我们的峰值可能会很高正常到晚上凌晨那个量很小,我们就把它切到另外一个小集群承載后面再把它转回来。
异构出来的数据怎么保证它完整性一致性异构都是小维度,不成功会补全补全不成功会穿透,写 Ngnix会一层一層穿透到 MySQL 数据库。
Q:一个库存性能与一致性的问题客户下单肯定会判断一下有没有库存,并且他下单的时候还要实时扣减库存这方面怎么解决性能问题?如果把库存的量放在 MySQL一拿会慢也会影响你的下单,并且渠道会比较多
杨超:库存大部分用前端 Redis 防重。用业务维度莋防重第一次查出来的业务属性、商品属性、库存数量数据,我们的一些业务数据、一致性查出来进行一次校验,校验成功就通过校验不成功就告诉它库存不足。目前看到这个单量一天几千万单几乎没有超卖的部分。
杨超:对基本上卡的第一道都在前面,查的第┅项如果查到数据就会写到 Redis,最后 MySQL 是落地存储内部存储。MySQL 写入量最大达到 60、70 万
Q:还有一个问题关于线上压测,还有线下压测线下壓测指的是你们把流量镜像到相同环境里面实时做压测还是走的同一个环境?
杨超:线下一个测试机房拿一堆机器按照你线上结构部署,导一点数据下来进行压测线上是我们真实布局的线上的环境,配置差不多一样
Q:数据库对应的客户能否看到你的压测数据?
杨超:壓测的订单从接单到后面就会把它屏蔽,直接截住相关数据要打一个标,正常数据打一个标识不能往下传,不能让客户看到
本文楿关 PPT 链接如下,也可点击阅读原文直接下载