「轻阅读」京东商城交易系统的演进之路

我是乔帮主 2019-11-28 13:23:45 ⋅ 821 阅读
原文:https://www.toutiao.com/i6762874634867048963

商城服务


如图所示是京东交易平台的一张大的渔网图。从主页面网站开始,到后面提交订单、购物车、结算页、订单中心等的整个生产过程,大体可分为三个部分。第一部分是订单提交前,就是俗称的购物车,结算页。第二部分是订单预处理部分,生成订单之后、到物流之前,还会有一些预处理过程,比如生鲜、大家电、奢侈品、易碎品等商品。第三部分是订单履约部分。今天我讲的主要内容是,交易平台的提交以前和预处理部分。


京东交易平台,包括单品页的价格、库存,购物车、促销,结算页的下单,再到订单中心线。


如下图所示,2011年京东的订单量是30万,2015年订单量就已经到了3000多万,京东的流量每年不断地翻倍。订单从30万到100万是三倍增长,实际上访问流量的翻番,可能是10倍、50倍,甚至上百倍。比如,用户购买东西从单品页进入,然后查询很多信息,包括价格、评价。商品加入购物车后,用户会不停地比对各类商品。刷新购物车,从前端到后端所有的服务基本上都会刷新。那么,当你刷新一次,调动服务就会承受一次动态的调用。当订单量翻三倍的时候,实际服务访问量最少是要翻20倍。


我见过的京东目前最大的前端流量是,一分钟几千万,一个正常前端服务访问量是在几千万,几亿、几十亿,一天的PV。

那为了应对如此大的调动量,每年的618、双11,京东都做了什么?

下面我会详细讲618、双11备战后面,每一年所做的不同改变。这是一个整体的大概分析,我们从哪些方面做优化,去提高系统的容灾性,提高系统应对峰值流量的能力。


实际上每年京东内部的正常情况是,领导层会给出一个大概的预期值,就是希望当年的大促中,需要达到几百亿,或者几十亿的预期销售额。那么,根据这个销售额,根据客单价(电商的订单的平均价格,称为客单价)换算成订单量。另外在以往的618、双11中,我们都会统计出订单量和调用量,即前端价格需要访问多少次,购物车需要访问多少次,促销引擎需要访问多少次,整个流程需要多大的量。有了大概的方向之后,就会把具体系统的量换算出来。第一轮会做压测,压测分为线上压测和线下压测两部分。这些都是准备工作,根据一些指标往年的增长量估算出一个预期值。

压测


这是真正进入第一波。首先,每年的大促前,都会经历半年业务迭代期,整个系统会有很多变更。我们会进行第一轮的压测系统,压测之后会知道当前线上真正能够承载的访问量有多大,距离预期有多远。压测分为线上压测跟线下压测。压测场景分为读业务和写业务,压测方案有集群缩减服务、模拟流量、流量泄洪。

讲到压测,先说说压测的来历吧。2011年时候没有线上压测,线下压测也不是很全。真正引入线上压测是在2014年,订单量已经接近2000万。之前的大促备战,是通过组织架构师、优秀的管理人员,优秀的技术人员,一起去评估优化系统,因为在迭代代码的同时,我们会知道系统哪里容易出现问题,然后对数据库、Web或者业务服务做一堆优化。

在2014年,订单量到了上千万,换算成为访问量,每天的PV大涨,集群也更大偏大,如果还是只依靠技术人员去优化,可能会不足。于是就衍生出压测,我们想知道系统的极限值。这样,当系统承受不住访问请求的时候,我们就会知道哪里出现瓶颈,比如,服务器的CPU、内存、连接速度等。我们通过第一轮压测找到第一波的优化点,开始了线上的压测。

当时第一波做线上压测是在凌晨一两点,把整个线上的流量剥离小部分机器上。把集群剥离出来,然后再做压测。所有的服务器、所有的配置就是用线上完全真实的场景去做压测,就能够得到线上服务器在真实情况,再优化。

曾经做redis压测,把进程绑定到单核CPU,redis是单进程程序,当时集群的性能就提升了5%。因为机器的每次CPU切换,都需要损耗资源,当时把进程直接绑定到固定的CPU上,让它高压下不频繁地切换CPU进程。就这样一个改变,性能提升了5%。当量很大的时候,真正底层细节的小改变,整个性能就会有很大的改进。这是我们从2014年引进线上压测和线下压测之后的一个真实感受。


压测完之后得到容量,得到交易系统的购物车、结算页大概承受值,之后会进行一轮优化,包括对NoSQL缓存的优化。京东在2012年的时候自建CDN网络,Nginx层做了很多模块加Nginx+lua的改造。应用程序层也会做很多缓存,把数据存在Java虚拟器里面。数据层的缓存,主要有redis、 NoSQL的使用,另外会剥离出一些独立的数据存储。

缓存 压缩


CDN域名切换的问题,原来外部CDN切换IP,需要15-20分钟,整个CDN才能生效。我们的运维做了很多的改进,自建了CDN,内网VIP等等进行缓存压缩。Nginx本身就有介质层的缓存和GZIP压缩功能,把静态js、CSS文件在Nginx层直接拦掉返回,这样就节省了后面服务的服务器资源。GZIP压缩能压缩传输的文件以及数据,节省了网络资源的开销(GZIP压缩主力损耗CPU,机器内部资源的平衡)。前面就直接压缩返回图片、文件系统等静态资源。流量到部署集群系统时,只需要处理动态资源的计算,这样就将动态静态分离集中处理这些专向优化。

真正的计算逻辑,服务自身的组装、如购物车的促销商品、服务用户,基本上所有资源都耗费在此。比如,连接数都会耗费在跟促销,商品,用户服务之间调用,这是真实的数据服务。如果不分离,你用DOS攻击直接访问JS,然后传一个大的包,就会完全占用带宽,连接和访问速度就会非常慢。这也是一种防护措施,在大促中会做很多缓存、压缩这些防护。


购物车从2010年就开始Java改造,整体结构的划分主体有,促销引擎、商品、用户。系统结构在2012年已经成型。到13年,加入了购物车服务的存储。原来购物车存储的商品是在浏览器端的Cookie里的,用户更换一台设备,之前加入的商品就会丢失掉。为了解决这个需求,我们做了购物车服务端存储,只要登录,购物车存储就会从服务端拿取。然后通过购车服务端存储打通了手机端与PC端等的存储结构,让用户在A设备加入商品,在另外一个设备也能结算,提高用户体验。

异步 异构


2013年之后,接入了很多其他业务,如跟腾讯合作,有微信渠道,我们会把存储分为几份,容量就会逐步地放大。这是异步的存储,手机端会部署一套服务,PC端会部署一套服务,微信端会部署一套服务。就会隔离开来,互不影响。

购物车就是这么做的。购物车整个数据异步写的时候都是全量写的。上一次操作可能异步没写成功,下一次操作就会传导都写成功了。不会写丢,但是可能会有一下延时,这些数据还是会同步过来。比如,从PC端加入商品之后没有立即同步到移动端,再勾选下购物车,购物车的存储又会发生变更,就会直接把全部数据同步到移动端。这样,我们的数据很少会出现丢失的情况。

异步写的数据是进行了很多的压缩的。第一层压缩从前端开始,整个前端是一个接口串,到后面购物车服务,先把它压缩为单个字母的接口串,后面又会压缩成字节码,使字节流真正存储到redis层里面。当存储压缩得很小的时候,性能也会提高。

缓存压缩只是为提升纵向性能做的改进。后面还会进行横向异步异构的改进,购物车把移动端存储剥离出去,移动端的存储在一组redis上,PC端的存储在另外一组上。PC端和移动端是异步去写,这样相互不影响,虽然它们的数据是同步的。这是针对多中心用户所做的一些改进。

外层的异步,是做一些不重要的服务的异步,就从购物车前端看到的地址服务、库存状态服务。库存状态服务在购物车只是一些展示,它不会影响主流层、用户下单。真正到用户提交的时候,库存数据才是最准确的。这样,我们会优先保证下单流程。


接下来讲讲接单的异步。提交订单,提交一次订单原来需要写10多张表。当订单量提高到一分钟10万的时候,系统就无法承受。我们就把整个提交订单转成XML,这样只写一张表,后面再去做异步。接单的第一步,先是把整个订单所有信息存储下来,然后再通过状态机异步写原来的10多张表数据。


关于订单中心的异步异构,订单中心原来都是从订单表直接调出的。随着体量增大,系统无法承载访问,我们异构出订单中心的存储,支付台帐存储等。异构出来数据都具有业务针对性存储。数据体量会变小,这样对整体的优化提升提供很好的基础。

这样的存储隔离,对订单状态更新压力也会减小,对支付的台帐、对外部展示的性能也会提升。大家会疑问,这些数据可能会写丢。我们从第一项提交开始,直接异步写到订单中心存储,到后面订单状态机会补全。如果拆分不出来,后面就生产不了。也就是说,到不了订单中心,数据生产不了,一些异步没成功的数据就会在这个环节补全。


然后是商品的异步异构。2013年,商品团队面临的访问量,已经是几十亿。如何去应对这个情况呢?很多商品数据贯穿了整个交易,包括交易的分析、各个订单的系统都会调商品系统。我们会针对系统优化。比如,针对促销系统调用,促销系统主要调用特殊属性,我们把这些属性存到促销系统的特有存储。库存系统也类推。调用的特殊属性的方法也不一样。譬如大家电的长宽高这些特有属性,不像前端商品页里只是基本属性。这样就把所有的属性异构处理,针对商品纬度、商品ID等所有数据会异构一份到库存、促销、单品页,后面进行改造的时候,又将数据分A包、B包、C包。京东的业务很复杂,有自营,又有平台数据,A包可能是基础数据,B包可能是扩展数据,C包可能是更加偏的扩展数据。这样,促销系统可能调用的是B包的扩展属性,也有可能调用的是A包的基础属性。单品页访问A包、B包,调的集群是不一样的。这样存储的容量就可以提高两倍,系统的容灾承载力也会提高。

商品原来是一个单表,后来慢慢发展成为了一个全量的商品系统,包括前端、后端整个一套的流程。异步异构完了之后,系统可进行各方面的优化,这样系统的容量也会慢慢接近预期值。然后找到系统容量的最大值,如果超过这个值,整个系统就会宕机。那么,我们会做分流和限流,来保证系统的可用性。否则,这种大流量系统一旦倒下去,需要很长的时间才能恢复正常,会带来很大的损失。

分流限流

在618、双11时候,手机、笔记本会有很大力度的促销,很多人都会去抢去刷。有很多商贩利用系统去刷,系统流量就不像用户一秒钟点三四次,而是一分钟可以刷到一两百万。怎样预防这部分流量?我们会优先限掉系统刷的流量。

Nginx层:通过用户IP、Pin,等一下随机的key进行防刷。Web 层:第一层,Java应用实列中单个实列每分钟,每秒只能访问多少次;第二层 ,业务规则防刷,每秒单用户只能提交多少次,促销规则令牌防刷。
从Nginx,到Web层、业务逻辑层、数据逻辑层,就会分流限流,真正落到实际上的流量是很小的,这样就会起到保护作用,不会让后端的存储出现崩溃。从前面开始,可能访问价格或者购物车的时间是10毫秒,保证20台的机器一分钟的流量是一百万、两百万。如果是40台机器的话,承载能力会很强,会透过Java的服务,压倒存储,这样会引发更大的问题,庞大存储一旦出现问题很难一下恢复。如果从前面开始一层一层往下限,就可以起到保护底层的作用。中间层出问题比较容易处理,如Web层,限流会消耗很多CPU,会一步步加入更多机器,这样就能够解决这个问题。

我们需要降级分流限流。


下面结合秒杀系统来讲讲如何限流分流。2014年才产生秒杀系统。当时,在同一时刻可能有1500万人预约抢一件商品,抢到系统不能访问。后端服务都没有出现这些问题,有的服务费不能正常展现。后来就专为抢购设计了一个秒杀系统。正常情况下,有大批量用户需要在同一时间访问系统,那么就从系统结构上分出去这些流量。秒杀系统尽量不影响主流层的入口,这样就分离出来一部分数据。


接下来讲讲促销和价格。主力调用价格的服务主要在促销引擎,限流主要是通过购物车服务,购物车到促销引擎又会限流,促销引擎里面会有令牌。比如,有5000个库存,发50万个令牌到前端去,肯定这5000个库存会被抢完,不可能再把其他服务的量打到后面,这样会保护促销引擎,这是一种总令牌模式的保护。后面的分流性能,会分集群式、重要程度去做。另外,一些广告的价格服务,我们会优先降级,如果出问题的话会限制。另外,有一部分刷引擎刷价格服务的数据,正常情况下是保证它正常使用,但是一旦出现问题,我们会直接把它降级,这样就保护了真实用户的最好体验,而不是直接清除程序的应用。

容灾降级


每次双11活动,我们会做很多的容灾和降级,有多中心交易、机房容灾、业务容灾等各种纬度的容灾。大概统计了一下做过的一些容灾方案。


首先是网络容灾。前面说到SB中间件、域名解析,我们运维自己会做了核心交换机两层专线。这是我们运维部做的一些网络架构图,两边相互容灾的一个结构。有LVS、HA、域名及解析,只是单服务挂了,通过交换机,我们可以从一个机房切换到另一个机房,因为会做一些域名的解析和切换。


应用系统相互调用容灾和降级:结算的容灾和降级。应用系统大部分能够降,比如库存状态。如果像优惠券这些不重要的服务,备注信息,可直接降级服务,不用去访问它,直接提交就行。在提交订单时候,首先我们会保证必要服务,这些服务都会有很多的保护措施。每个应用里面,应用级别、服务级别的容灾,比如地址服务、库存状态容灾可以直接先降级。到提交的时候,我们直接对库存做限制。


应用内部的容灾。库存就是结算前面的系统应用的服务,再到细一层的我们的库存服务,这是每一个服务的容灾降级。从库存状态这边的话,从网络设备内层,有网络容灾降级。应用内部有对于预算服务的降级,预算服务会有预算库存,原来是写MySQL数据库。正常情况下,预算库存是写MASIC预算库,当出现问题的时候,我们会异步堆列到本地机器,装一个程序去承载这个异步MySQL数据的落地,然后再通过Work把它写到MySQL服务里面。正常情况下,是双写MySQL、redis,当MySQL承载不住的时候,我们会把MySQL异步写到里面。

这里面都会有开关系统去控制。当提交订单产生变更的时候,才会把库存状态从这边推到这个库存状态这边,因为库存状态的调用量跟价格一样很大。今年我们看到的最大调用量是一分钟2600万。这样不可能让它直接回原到MySQL,跟直接库存的现实存储里面。通过预算系统把这个状态从左边算好,直接在推送过到真正的存储,这样就把这个存储剥离出来,这也算一种异步异构,这样我们会提升它的容量。

这是原来的结构,就是redis直接同步,然后直接访问。现在把它改成是,直接让左边的预算服务去推送到状态服务里面。

监控


最后主要就是监控系统,我们运维提供了网络监控、机器监控。

网络监控包括我们看到的SBR,以及一些专线网络监控,如交换机、柜顶交换机、核心交换机的监控。


接下来是应用的系统监控。机器监控有CPU、磁盘、网络、IO等各方面系统的监控。业务纬度的监控,有订单量、登陆量、注册量等的监控。京东机房微屏专线的一个网络平台的监控,里面有很多专线,它们相互之间的流量是怎么样?图中是我们监控系统,是机器之间的监控,包括机器直接对应的交换机、前面的柜机交换机等的网络监控等。

这是应用系统里面的方法监控,后面是业务级别的监控。订单级别的包括注册量、库存、域站,或者区域的订单、金额、调用量的监控都会在这里体现。真正到大促的时候,不可能经常去操作我们的系统,去修改它的配置。正常情况下都是去查看这些监控系统。2012年之后,监控系统一点一点积累起来。当量越来越大,机器资源越来越多之后,这些监控都会直接影响正常服务,服务用户的质量会下降,可能20台机器宕了一台机器,不会影响全部效果。所以,监控的精准性是一步步慢慢提高的。



全部评论: 0

    我有话说:

    京东到家订单中心系统mysql到es转化

    原文:https://www.toutiao.com/i6796507988602389006 京东到家订单中心系统业务中,无论是外部商家订单生产,或是内部上下游系统依赖,订单查询调用量都非常

    商城系统 DBShop V3.0 Beta 发布

    全新重构,首次亮相。 系统简介 DBShop企业级商城系统,使用PHP语言基于Laminas(Zendframework 3) + Doctrine 2 组合框架开发完成。可定制、多终端、多场景、多

    阅读」图文并茂带你了解分布式架构演进

    初始阶段架构初始阶段 小型系统 应用程序、数据库、文件等所有资源都在一台服务器上通俗称LAMP

    阅读」推荐系统中信息增强小技巧

    实用推荐系统构建经验,如何进行信息增强。

    阅读】为什么越来越多系统在做服务化?

    脱离业务实际情况架构都是耍流氓,所以不是所有系统都必须服务化,也不要为了服务化而服务化。

    阅读」“做完”和“做好”区别

    在工作中,“做完”和“做好”虽然仅一字差,但前者只是完成了某项工作,而后者则不仅是完成了工作还有一个好

    阅读」微博推荐架构演进

    微博两个核心基础点:一是用户关系构建,二是内容传播,微博推荐一直致力于优化这两点,促进微博发展。

    A3Mall v1.5.2 发布,免费开源 PHP 小程序商城系统

    项目介绍 A3Mall商城系统是基于ThinkPhp6.0+Vue开发一套移动电商系统, 支持微信公众号商城、H5商城、小程序商城,支持多种营销活动,优惠劵、订单活动、团购、秒杀、会员特价、积分

    阅读」如何构建可伸缩Web应用?

    可伸缩性已经成为Web应用程序DNA!

    阅读」轻松理解 Kubernetes 核心概念

    Kubernetes 迅速成为云环境中软件部署和管理新标准。

    阅读」大众点评是如何分表分库

    原大众点评订单单表早就已经突破两百G,由于查询维度较多,即使加了两个从库,优化索引,仍然存在很多查询不理想

    阅读」Mysql调优你不得不知细节

    多数时候数据库会成为整个系统瓶颈

    阅读」亿级用户分布式数据存储解决方案

    分布式数据库和分布式存储是分布式系统中难度最大、挑战最大,也是最容易出问题地方。互联网公司只有解决分布式数据存储问题,才能支撑更多次亿级用户涌入。

    阅读」分布式事务四种解决方案,成长需要尝试

    分布式事务指事务操作位于不同节点上,需要保证事务 AICD 特性。

    阅读」为什么在做微服务设计时候需要DDD?

    设计蓝图里为什么没有看到DDD影子呢?

    阅读」移动端适配必须掌握基本概念和适配方案

    随着技术发展,移动设备越来越流行,并且不同设备间屏幕尺寸和屏幕像素差异,移动端开发面临着多分辨率适配问题。

    阅读」移动端事件穿透原理与解决方案

    本文将带你了解事件穿透及如何在实际项目中选择合适方案解决事件穿透问题。