作者:尤凤凯, 京东商城研发-交易平台-商品研发负责人。2010年加入京东,先后参与设计研发京东第一代监控、消息、EDM等系统。12年开始致力于商品系统SOA化、商品系统的持续架构演进。现主要负责商品中台及组件化建设。
商品,黄金交易流程最基础、最核心的环节,无商品不电商。商品数据无处不在,商家(采销、供应商)发布管理、供应商下采购单、仓储配送、促销、搜索、商详页展现、购物支付、财务结算、售后服务等,都离不开商品。商品信息要准确传导于京东整个供应链的各节点,必须要有一套稳健、可靠的商品服务体系支撑。
原本并没有统一的商品服务及存储。DBA搭建了一套包含若干层级的SqlServer数据库复制结构,各系统从各级从库读取数据。复制延迟严重的时候,超过12小时,从库还没有更新,严重影响用户体验。写入口不止一个,获取到写账号就可以写入。erp商品管理系统、POP商品系统、BIP甚至也开发了一个商品管理系统,都在写同一个库的同一套表,数据一致性无法得到保障。在那个阶段京东的订单量、用户量相对较少,基于数据库的架构一定程度上也能支撑日常流量,但是无法应对大型促销活动。
在此背景下,2012年3月商品组从网站组独立出来,孙歌临危受命组建团队,启动商品技术架构升级项目。京东618年中狂欢购物节,系统(特别是0点)会承受平时无法比拟的压力。2012年之前的大促都会出现系统问题系统经常出现问题,甚至图书抢购活动时直接系统宕机。基于数据库提供读服务的架构,显然已经无法支撑业务前行,升级改造势在必行。12年初NoSQL还是新鲜事物,交易架构师龚杰开始实践Redis,他在一封邮件中介绍自主封装的客户端以及API。商品团队开始基于redis内存数据库搭建商品读服务,并对开源Jedis做了深度封装,扩展了ShardedJedisPool,实现了更加细粒度的多分片连接池管理,并且将一个请求中命中到同一个分片的redis命令由串行改成pipeline并行执行,性能提升较大。
架构1.0 读服务化
由Redis存储全量商品数据,作为内存数据库使用。商品信息变动时增量更新,一主多备模式容灾,同时全量刷新程序作为最后保障,一旦Redis中数据全部丢失,可以将商品库中数据恢复到Redis。
交易系统直面用户,为保证用户选品、下单结算的顺畅,提升用户体验。交易系统对高性能、高可用的商品读服务需求最迫切。此时架构升级采取了一种“轻模式”,所谓轻是指尽量减少外部系统的改造,这样更利于工作的快速推进。首先在通知模式上,各种商品系统写入Sqlserver主库后,通过HttpHTTP服务通知Rcat -server(采取尽量做的策略,能通知就通知到,有异常也不去重试,这种策略对相关系统的改造最小)。Rcat-server的职责就是接收通知,之后查询主库中的商品信息,将其更新到Redis中。因为写入系统是尽量做,不保证成功的模式,因此需要补偿机制去弥补遗漏。异步Worker会定期扫描数据库,把当日更新的,从刷新到Redis中。整体架构如图1-1所示:
图1-1 商品架构V1.0
V1.0版架构非常不完美,只是读服务这个点进行了改造,但是却在当年618中完美的完成了任务。618当天像往年一样,研发工程师售后守候在运维同学周围,时刻准备着!整个过程波澜不惊,只有过个别应用过载重启,整体非常顺利扛过了大流量的考验。有了这个经验,研发工程师们开始将Rcat(应用名称,商品读服务)推广,依赖Sqlserver从库的系统都逐步切换到读服务上。
架构2.0 全面服务化
POP商品系统和自营商品系统都是写入Oracle,在通过异步worker将数据写会Sqlserver。京东商品的独特之处在于最初是自采自销的自营类商品,有自己的商品模型和对应的erp管理系统。后续有了POP开放平台,该平台的商品模型和系统是独立搭建的,适应于第三方商家的商品发布管理。而所有下游的系统(单品、搜索、订单生产、仓库等)都是基于最初Sqlserver自营skuSKU模型做的系统设计。所以POP商品有自己的Oracle存储,也必须京东经过一步异步程序转化,写到Sqlserver商品库中。而自营商品系统在2011年架构升级时,计划由.Net+Sqlserver的技术体系升级外Java+Oracle技术体系。
孙歌作为研发负责人,基于技术前瞻性和成本考虑,果断决策放弃既昂贵又没有DBA团队良好支持的Oracle数据库。转而直接基于SqlServer实现商品的全面服务化,相比其他系统的去O足足早了一年。当时的整体思路是先实现服务化,再进行存储升级(Mysql集群),最终完成彻底的技术架构升级。全面服务化过程:
1). 全面读服务体系:
将下游系统读取的信息刷新到Redis中,由读服务Rcat统一支持根据skuId查询的需求。对于检索需求,例如根据分类id查询SkuSKU列表,搭建Solr索引服务。基于各系统的需求收集已经当前SQL的分析,搭建了读服务体系,并逐步推广,去掉各系统对Sqlserver数据库的读取依赖。
2).写服务化
接管所有商品写操作,提供商品相关的基本读写服务,建立商品主数据中心,服务于自营商品管理、POP平台、图书音像商品管理等系统。京东起家于3C自营产品, SKU化管理。后发展的POP平台,是商品化发布管理。写服务必须兼容两套模型,平滑过渡。研发工程师最终设计为统一到商品-skuSKU模型,自营商品与SkuSKU一对一,而POP商品与SkuSKU是一对多。自营商品每发布一个商品有且仅有一个SKUSku,pop商家发布一个商品可以有多个SkuSKU。自营商品沟通通过后期的颜色尺码绑定,实现销售关系捆绑。这样展现给用户的效果是一样的,同时对于京东采销、POP商家都可以按各自的习惯去操作商品。
写服务设计时架构师尤凤凯充分考虑了扩展性、可复用性,实现了模块可配置化。基于商品介绍、扩展属性、规格参数、特殊属性等基础组件,可灵活组配不同的业务流程。使用了京东自主研发的SAF中间件,其支持负载均衡、自动故障切换、流量控制、黑白名单等特性,并且在性能方面表现优异。
3).去O:去掉自营商品系统的Oracle,直接写Sqlserver降低延迟,缩短商家发布到展现给最终用户的时间。
4).异步引擎:建立下发服务系统,统一化消息通知网站、WMS等下游系统。
最初的商品变动时,只需要通知WMS系统, 因为采购入库时,仓库需要核实商品信息。随着整个京东服务化进程的推进,搜索、单品页等系统都希望得到通知。因此规划了下发服务,在商品信息变动后,异步通知这些系统。将商品信息入库后,同步写”操作日志”到Redis队列,再由worker异步从Redis中取日志,拿到变动变更的skuSKU,组装信息化发MQ消息出去。此时的消息采取通用化设计,例如基本信息MQ、特殊属性MQ等。
5).存储升级:商品主数据存储由Sqlserver单库,升级为MySQL集群,并进行垂直、水平划分, 分库解决单库吞吐量瓶颈,分表控制单表数据量。自主研发数据中间件,可以实现主库的路由、从库的负载均衡、故障的切换等,统一负责数据访问,使得底层存储规则对应用层透明。结合当前数据总量及增长率,预计3年后达到的数据量,做存储容量规划,并且做了Pre-Sharding方式,方便后续扩容。对于非片键外的查询,使用二级路由或者搜索服务来解决。
整体架构如图1-2所示
图1-2 商品架构v2.0
随着商品及SKU数量显著增长、TPS逐步走高。写服务、下发服务耦合的弊端越来越突出显现。如1-2图中红色线条形成的环状所示,写服务和下发服务是相互影响的。假设写Redis变慢,会影响整体写入性能;如果DB遇到瓶颈,反过来又回会影响到下发服务,回查DB变慢,下发服务处理变慢。因此写服务经常会出现抖动,写变慢、下发延迟。
架构3.0平台化、精准化
2015年中开始,架构师李帅启动3.0架构升级,其理念是解耦、简化。架构越简单越好,没接触过的人新人很快可以上手;架构也必须松耦合的,写、下发、读都可以各自做升级,相互不影响,逐步提升整体性能。
用Jingobus基于从库日志识别商品信息变动,并发送通知、刷新redis集群。异步引擎消息可配置化,商品属性的组合对应消息队列。例如:搜索关注skuSKU状态变化,那么只有skuSKU的上下柜状态变化时,才会发送消息,消息体中只包含skuSKU编号及状态。可配置化的任务引擎,新需求响应快、开发接近零成本;实现了按需发送,给消费方减压(例如:给wms的消息量降低80%+);并且写系统解耦,整体稳定性增强。在推广过程中,还进行跨部门技术协作,共同升级技术架构。例如网站单品页数据异构升级项目,降低50%+接口交互,节约上百台Docker。
商品服务作为超0级系统 ,必须具有高可用性。任何影响到订单的系统故障都必须在三分钟内解决,有了统一切换平台,执行预案是可以很快的。因此发现问题并警报至关重要。在研发负责人赵湘建的引领下,商品组启动秒级监控平台的研发。内存级监控信息收集、合并、延迟秒级。并且实现了秒级监控的平台化,可将监控能力输出到其他系统。
期间商品读服务也在持续进行着升级。因为skuSKU数量大(数十亿)且持续增长(周增长率约105%),Redis存储集群规模也大。读服务为其他众多系统提供商品数据的查询服务,访问量很大,特别是在618,双11期间,所以需要多组Redis集群分担流量。NIO的Redis客户端,降低了连接数量;后续为解决多组Redis集群流量均衡问题,对NIO版本的客户端做了扩展,实现了多分片连接统一管理,使其负载均衡,并能当某一分片宕机的情况下,自动从集群中剔除,恢复后自动加入集群中,达到故障自动转移与恢复的目的。不仅提高了集群整体的吞吐量,而且提升了可靠性。
同时因为商品数量增长很快,Redis集群的规模也成倍增加,为减少资源的利用,设计三级缓存功能,将最热数据放应用内存中,缓存时间较短;热数据放在规模较小的Redis集群中,全量数据放到规模较大的集群中,这样全量数据的Redis集群OPS较少,可以减少部署组数,从而减少资源利用。
图1-3 商品架构V3.0
京东商品系统在业务创新、数据智能化、性能及稳定性提升方面,将持续努力提升,努力实践让技术改变生活的愿景。
更多技术架构资料可参考《亿级流量》。