京东开放平台(POP)架构解密

大家好,我是今天的分享老师:王栋。我来自京东商城,是京东开放平台(POP)的架构师,有幸参与和见证了京东开放平台(POP)架构的三次变革。

很高兴受到“京技院“的邀请,能有机会与大家交流京东开放平台这些年的成长经历,也分享一下京东开放平台的架构是怎样一步步跟随业务的脚步完成蜕变。第一次参与这种文字直播的分享,有哪里描述的不清楚的还请大家见谅。不明白的后面QA环节,大家多多提问吧。

我今天的分享主要分为三部分:

  1. 京东开放平台(POP)的介绍

  2. 京东开放平台(POP)三次架构演进

  3. 两个典型业务场景下的架构设计方案

京东开放平台(POP)的介绍

首先我先介绍下京东开放平台(POP)。

京东目前两种经营模式,第一种是自营,即京东自己采购自己销售,第二种就是指开放平台(POP),即第三方商家利用京东商城网站销售商品。

如上图,O2O本地生活服务、京东国际化、团购、虚拟业务(机票彩票)和闪购都是依附于京东开放平台(POP)业务体系和架构体系的新型模式。

能够承载如此多的业务模式,又要面对京东每年的高速成长,我们京东开放平台(POP)的架构是怎样一步步走向成熟,并始终坚若磐石的呢?下面一个部分会慢慢的展开它神秘的面纱。

京东开放平台(POP)的三次架构演进

V1.0版本

2010年的时候京东开放平台(POP)成立,当时的要求是满足商家的入驻京东、商城销货、商品管理、订单管理等几个核心的业务场景就可以了,而且要求尽快上线。时间对于一个创业公司来说就是生命(当时京东处于创业阶段)。当时不足十人的情况下,短短一个月的时间京东开放平台(POP)的第一版就正式上线运营了。

我们通过画一个图来描述下V1.0版的架构,如下:

整个架构就分为了两部分:业务层、数据层。业务层又分为两个系统:运营后台是给京东小二用的、商家后台是给商家使用的。然后业务层产生的商品等数据直接提供给网站使用。

技术选型上,业务层使用当时最流行的Apache + Tomcat,数据层采用了最可靠的Oracle小型机 + FastDFS(存储小文件)。

代码工程管理上,团队几个人在同一个系统上开发,兄弟们一起开发一起测试一起上线,共同奋斗几十个日夜,保证了系统的稳定快速的一次次上线。现在看起来这个架构很简单,可是在当时时间就是一切的背景下,这个架构是可靠的适合的。

在座的如果有创业期的朋友,要留意了,适合的架构才是最好的架构,千万杜绝完美主义。

V2.0版本

第一个版本虽然简单,但是也在京东线上运行了1年多之久。

当时的POP业务已经进入高速发展阶段,销量增长非常迅猛。商品和SKU数目指数增长,经营不同类目的商家差异化要求严重。兄弟们几乎天天加班,逢周二周四上线日经常通宵达旦。在2011年底的时候,大家再也忍受不了。架构升级被提上了日程。

首先我们梳理下V1.0版本最主要的问题:

1.团队增大,同一系统开发,一个人改动所有人跟着上线,系统耦合太大。耗时耗力并且每次上线风险很难控制。

2.很多操作直接打到数据库,oracle再牛也扛不住了

3.后台太多异步化处理逻辑,并且散落在系统的各个角落,难以管理

2012年初的时候我们进行了规模最大的一次升级,升级后的架构如下图所示: 这次架构升级围绕的核心也是解决V1.0版本中所有面临的主要问题。

第一个就是采用微服务架构,把之前的一个系统按照不同业务线拆分成了多个子系统(如:商品系统、订单配送系统、商家入驻系统等等),各个子系统之间充分解耦,独立开发独立部署。但对于用户来说,感觉上还是在一个系统中进行操作。

第二个是开发了一个异步work调度管理系统。这里先讲解一下什么是work机制:简单说就是后台异步处理,目的是增加用户体验,例如用户做了一次操作后迅速提示完成,但是其实后台会触发多个任务,每个任务都会对应着独立的后台异步处理逻辑。V1.0版本中,后台大量的work在执行,散落在系统的各个角落,而且每次执行都会去扫表。这样导致work难以管理,对数据库的压力太大。

这个系统很好的解决了大量work扫表的问题,让异步任务更合理的运行,而且能很好的监控每个work的执行情况。技术上主要是利用了NoSQL,进程间通讯等技术,时间原因不深入讲解了,有机会也可以整理相关的课程给大家详细分享。

第三是加入了很多缓存机制,例如前台访问的流量不允许直接打到库上,复杂查询的调用全部使用Solr不允许查库等等。

第四是系统开放,也是我最主要负责的一块。2012年的时候大量的个性化需求得不到满足,于是我们觉得需要引入第三方开发者(ISV),让这些社会资源能够一起为京东和京东的商家提供服务,打造一个完整的卖家生态。

开放服务的系统采用的独立部署,提供Restful API服务,在安全上采用oauth2.0协议。为了保证系统稳定还加入了很多限流、降级等措施。为了提高开发者效率,还提供了沙箱测试、开发者中心等等服务。

这时我们的数据层也不在单一依赖于Oracle,采用了更多元的解决方案,例如缓存Redis、MySQL、MongDB、MQ、Slor、hadoop等等。线上的部署结构还是采用的单机房多实例的方式,只是这时我们告别了Apache时代,全部换成了并发性能更好的Nginx。

这一次升级后,我们系统的稳定性、易用性都得到了很大的提升,团队开发和上线流程也得到了优化,也通过这次调整我们可以更好的满足日益壮大的POP业务发展。后来发展出来的多种业务模式(O2O、国际化等等),都是在这个新架构上面衍生的。

V3.0版本

这个版本的关键词是:服务化、规范化、统一化。如图: 这次升级主要有下面几点:

1. 基础服务SOA化 打造电商核心API

上面介绍了,在V2.0版本中系统已经按照不同的业务进行了垂直的划分,内聚的业务放入一个子系统中。但是随着京东业务模式的不断增多,需要横跨不同子系统的业务模式越来越多,网状交织,系统有复杂了起来。面对这种情况,我们提出了服务SOA化,把基础的电商服务进行梳理,提供出一个个API服务提供给上游系统调用,所有API服务通过JSF(京东自研的RPC框架)进行统一治理监控。从而打造一套电商核心业务API。

2. 去Oracle

当京东业务量提高到一个数量级的时候,我们发现Oracle神机都扛不住了。这时候我们进行了去Oracle,采用了支持无限横线扩展的mysql,之前上亿一张表被按照某种规则散列到不同的mysql库和表里,进行了大量的分库分表和夸数据库事务的处理。

3. 内部实用工具的兴起和统一

统一配置中心:集中管理系统的配置、开关、降级、限流等的公用内部开源系统。

UMP统一监控平台:对系统的异常情况(比如:方法可用率、方法性能、系统存货、JVM异常、服务器CPU等等)进行展示和报警。

其实有很多好用的工具,先列举两个吧,这些工具的兴起大大提高了人效,也让每个研发人员对自己的系统更加的了解。


除了上面的三点,还有一些其他的方面的升级就不一一详细介绍,比如:缓存从各系统自己部署redis、memcahe到同一使用JimDB,大数据产品引入京东数据集市等等。

这次升级不是像V2.0那样大规模的统一进行架构调整,而是逐步的渐进式的。但每一步的改变都让我们的系统更加的稳定高效,让我们的开发效率得到了提升。

我是这样理解V3.0的,规模化之后的统一是必然,统一和规范也让我们更加专业。

同时我们也变的越来越开放,越来越拥抱变化。也正是在这个阶段,京东从不断的学习成长,开始变的愿意走出去了,愿意把京东的经验回馈给行业。

两个典型业务场景下的架构设计方案

上面讲了京东开放平台(POP)从上线到现在经历的几次架构的演进,每一次演进都是特定时期特定场景下,为了解决现有的问题而发生的。作为架构师根据业务场景,设计出最适合的架构,选择最合适的技术才是最重要的。

下面一起讨论两个特定业务场景下的的案例。

纯干货来了哦~~~~

案例一:海量商品sku存储

首先描述下业务场景,如下:

电商业务核心的一块就是商品展示和交易,这个业务场景我们讲一下所有在线商品和SKU的存储。京东开放平台(POP)中有海量的货品信息,这些信息需要在前台展示,有些信息比如图片和商品描述等,可以做CDN和静态页缓存,而另一些比如库存、价格等则对实时性要求非常高。

这些海量的货品信息除了给买家展示以为,还要给商家和京东小二进行多维度的多字段查询。

针对上面的场景,我们分析下这样系统的特点:

  • 数据量大
  • 实时性较高,商家改动价格和库存后网站上要尽快的变动
  • 数据更新比较频繁,商家会频繁的对商品信息进行修改
  • 要支持时时的复杂查询
  • 复杂查询访问量比较大,穿透前端缓存过来的调用量很高, 但是这些调用量的峰值比较平稳

分析了上面的特点,我们再来看看针对这些特点是怎样进行技术选型和架构设计的。

首先这些商品的信息是绝对不允许丢失的,所以存储上面一定要采用数据库,在数据量大的时候我们一般使用mysql数据库的分库分表。我们的商品系统就是采用的mysql数据库,分表策略是按照商家维度进行划分,总共分了2个库,每个库1024张表。对于一些分布不均匀的表我们采用再次拆分的策略,比如可以指定某个大商家独立占用一张表或者几张表。这些分库分表策略都是可以动态配置的(这些动态分库分表的小组件也行后面会开源哦)。

存储说完了,再说一下我们用来抗量的缓存。商品系统有一个特点,就是热数据少冷数据多,也就是说虽然有海量的数据,但被频繁访问的大概10%。面对这种情况,我们果断采取了热数据缓存的策略。商品系统采用的是Redis无持久化缓存,缓存有效期为1小时。这层热点缓存策略上线后,缓存命中率达到95%以上,很好的保护了数据库。在缓存更新策略上,我们采用了更新删除策略(商品信息被更新后直接删除缓存)。

最后说一下关于复杂查询。我们在面对复杂查询的时候,一般都采用Solr或者ES等基于Lucene的Java搜索引擎服务器。Solr和ES都可以支持横向扩展,所以不用担心数据量高速增长时的扩容问题。商品系统采用的是Solr(16台机器16个分片的集群,主备备的结构),存储了全量的商品数据来支持复杂查询的场景。Solr的更新策略我们采用了异步work的方式。

商品系统的整体设计如下图所示: 除了上面提到的那些点之外,在设计高可用系统的时候,除了正常的流程之外我们还要考虑好降级策略,限流策略等等。这里就不深入讲解了。

案例二:商家中心

还是首先描述下咱们的业务场景,如下:

商家中心是一个单纯对外提供查询京东第三方商家信息的服务,整个京东所有需要获取商家信息的系统都要调用该服务。例如京东购物车、单品页都会调用。

针对这种业务场景,咱们分析下系统的特点:

  • 商家信息变动很小,对实时性要求也不高,只要保证最终一致性就好
  • 读写比100/1,所有对外提供的服务都是读服务

  • 商家中心访问量比较大

  • 购物车 详情页移动店铺多处核心业务调用,稳定性和可用性要求极高

对于这种业务单一,调用量大,对稳定性和可用性有很高要求的系统,我们在设计的时候一定要让系统足够的简单。商家中心系统直接采用了Redis全量缓存、永不失效的策略,所有调用全部依赖于Redis,就算缓存没命中也不会读库,而是直接返回空,从而很好的保证了服务的高可用(为什么要这样做呢?因为在调用量极大的情况下一次穿透到数据库的查询都会导致整个服务可用率的降低)。

这里还要提一下我们今年618备战时的一次优化的故事。618前因为促销的原因,对服务调用影响很大。于是团队所有人马上召集一起进行深入的分析。最后发现了问题:我们的服务是多机房部署的,而我们的Redis也是,所以有些调用会产生跨机房问题,平时调用量少机房间网络IO小的时候问题不大,但是赶上整个京东所有服务都满负荷运行的时候问题就来了,一次跨机房的访问就直接导致问题的发生。

于是我们当天晚上就更改了缓存的部署和访问策略,如下图所示: 更改后的策略变为,统计方的服务调用同机房的Redis。而且增加了一层JVM级别的热点缓存,缓存时效为1分钟。新的方案上线后性能得到极大的提升,TP99都在2ms以内。

商家中心线上缓存Redis的线上部署情况,如下图:

  • 4台内存服务器
  • 48个分片96个实例
  • 采用Hash的存储方式
  • 当内存DB使用,无持久化