document.write("





























");

互联网+技术 京东青龙:物流配送系统的高可用服务

互联网技术联盟2019-06-10 07:45:41

电商时代,物流速度及售后体验甚至会决定用户的购物决策。很多人都很喜欢京东“极速达”、“211”这样的服务,但是看似简单的库房发货和用户收货之间,却隐藏着一套复杂的物流配送系统,京东称之为“青龙”。


用户下了订单后,在出库前,如何就能根据下单的区区几行信息就可以得知将由哪个站点甚至哪个配送员来为您配送,以满足所有用户对极速物流的要求呢?这正是青龙的重要子系统---预分拣系统,所谓物流的鹰眼。


青龙预分拣系统,是订单生产的重要依赖环节。信息流位置位于仓库生产、分拣中心操作前,同时也是订单时效重要保障的零级生产系统。简而言之就是根据用户的下单信息来计算最佳匹配的配送站点。首先介绍一下整体的架构图。




本文主要和大家分享其中的技术细节,让我们先从最常见的数据持久层说起。


一.数据持久化层---最后的防线


所有大型的SOA系统,往往数据库会是最大的瓶颈。这体现在架构设计时对数据库的严重依赖。之所以依赖,是由于数据库的关系型设计适用于一些数据的检索、修改、批量提交等业务场景,故广泛应用于软件系统中。


但是数据库也同其他事物一样是有两面性的,当数据量很大,并且高并发时,不当的sql或是索引就会导致整个数据库的锁死,而当灾难来临时我们可以在数据库层面的控制权小的可怜。所以,数据库是性能的一个关键节点,是系统最后的防线。那么我们如何来充分利用数据库,而有效规避不足呢?


1. 缓存的合理应用-为数据库减负,多级缓存同步


a) 很多系统都有这样的场景,数据的读操作远大于写操作,且业务对写操作的实时性要求没有那么高。这样频繁的读取数据库必不是好事,倘若遇上几个没有走索引的sql,那么数据库的cpu 很快就会爆满,而此时缓存便非常适用了。


b) 确定了用缓存来代替数据库的读负载,这仅仅是第一步。缓存用centralized cache还是local cache?集中式缓存势必会带来额外的网络开销以及需要承担额外的分布式结构带来的影响,而local cache往往非常脆弱,持久化困难等缺点。没有完美的方案,都是选取最适用的场景。下面举两个例子。


i. 预分拣地址库,存储着京东所有用户的下单所有地址。而预分拣匹配的一个重要步骤就是需要频繁的读取地址库。这个庞大的库若采用local cache,那么它耗费的memory足矣大于几十个应用的占用量,且当应用发生memory leak时必然会对其造成不可估量的影响。


ii. 预分拣生鲜订单的配送配置,是每一单生鲜订单都必须频繁读取的配置信息,是根据一二三四级地址和订单属性来识别站点关系的配置。那么一二三四级地址改变的几率很小,而站点变更的机会就更少了。且所有数据加起来还不到1w条,那么每次读取local cache最为合适。


2. 缓存的选型---生鲜配送配置实战。


a) 刚才说到了生鲜订单的配送配置,用本地缓存方式应付频繁的读更为合适。可是本地缓存是极其脆弱的,它没有很好的持久化策略以及分布式支撑,当它挂了怎么办呢?本地缓存的数据来自于哪里,若是有几十甚至上百个实例并行发布时,都需要从数据库load一把也会额外的提升数据库的瞬时负载。


b) 所以,是集中式缓存尽显文韬武略的时候了。我们缓存的读取和写入的策略如下图所示:


c) 上图中的instance就是大家的应用程序部署实例


d) 数据库的数据全量同步到centralizedcache,instance初始化时也从centralizedcache里同步一份到local cache,由此全量缓存化完成。


e) 那么数据发生变化如何增量同步缓存呢,可在数据库实时操作新增/更改时同时写入集中式缓存,各个实例可以用timer等schedule机制定时从集中式缓存里同步最新数据。


f) 读取缓存时,则与写入正好相反。这样虽然整套链路有些长,但是足矣抵挡绝大多数数据库的实时访问量,大大减少了负载。且从业务层面,是一定可以满足预先配置,新增和修改都不需要实时生效的。


g) 决定了缓存的同步策略,再来选取缓存的数据结构。无疑,更加注重性能的情况下会选取非关系型数据结构支撑的缓存,如redis、ssdb等。


h) 而redis又可进一步拓展,众所周知,redis的replication是非主线程做的,那么可以复制出来多份slave分片,除了原本的master-slave模式外,还可以抽出一份slave组合成一个集群,用于只读的服务。这样便可进一步的分散集中式主缓存shard的并发压力。


i) 在多条redis命令需要捆绑完成业务时,且数据可以shard在一个分片上,建议用lua把多条redis的众多操作封装起来,做一些数据的组装与拼接,并发效率的提升着实让人惊喜。


3. 复杂查询的缓存


a) 刚才提及的配送配置,主要是基于key-value、set、map、list等简单并且效率很高的数据结构的。而预分拣逻辑中有一些数据需要不断变更检索条件,会涉及到模糊匹配,那redis 、ssdb、hbase、cassandra之类的nosql产品就无法满足要求。此时,基于lucene的产品就非常适合了,solr也好es也罢,都能满足大数据查询的复杂条件要求,对数据库最怵的like正是拿手菜。


4. 数据库优化二三事


a) 由于这部分很多书籍都有,这里只说几点容易被忽略的。数据库除了常见的慢sql之外,就是索引的问题出现的最多了,尤其集合索引。要特别在意索引的b-tree何时进行旋转。


b) 合理利用存储引擎

i. mysql memory可以很合理的应用起来,查询效率是innodb的六倍还多。


c) 合理进行读写分离

i. 数据库一主多从,每套从实例可以单独作为只读数据源来承担对数据变化实时性敏感不高的业务。拆库主要目的是一定程度上减轻cpu的负担。我们有个兄弟系统,做bi的,在双11 期间临时把几张访问量很大的报表都做到了一张报表一个数据源,便很轻松的应对了压力。而除此之外任何一种方式都不会这样简单有效。


d) 跨机房灾备

i. 跨机房灾备本身的技术方案网络上到处都是,其主要问题,主要是在主从切换的一霎那会引起数据的丢失,需要提前做好重试方案,防止丢失数据而引起的故障。



二.灵活的配置中心---应急预案的处理与保障


当一个新的功能要上线,希望只是上某些区域时;当一个重要依赖的接口需要升级,担心不稳定会造成生产事故时;当新上线的功能需要市场检测,有问题就要随时下线时…这些场景都需要有灵活的开关来控制。


配置中心需要满足这样几个条件:

1.开关可以动态的进行灵活配置,配置之后不久便可以生效;

2.开关设置后,不需要重启应用可以自动同步配置;

3.开关可用性要高,也就是开关所依赖的存储单元要做到高可用。


用数据库的配置表来做开关最便于web页面的操作,且可以配置较复杂的开关逻辑,如根据省市县等参数来判断,根据预定的状态来判断。配置存储好了,若是每次读取配置都读取数据库,那这个配置反倒成了瓶颈了。


我们采用了Mina框架实现了tcp长连接的接入,用tomcatcomet机制实现了http长轮询服务。数据库中的配置发生任何改变时,都可以在一分钟之内同步到每个实例所访问的集中式缓存。读取配置的顺序是顺利成章的变成了先读取本地缓存,若本地缓存没有再去读取集中式的缓存。集中式的缓存有持久化,有fail-over和fail-switch,进一步提升了可用性。由此,配置即灵活又不会成为现有功能的障碍。


三.海量数据的盛宴,预分拣地址库


预分拣地址库有很大的数据量,如果用4号字体A4的纸打印出来,铺满在100平的房间里,可以堆三十层楼那么高。我们先看看第一版的架构吧。




cache proxy上面都是一些客户端的调用,有很多种形式。第一版的架构重点还是缓存的高可用方面,但是复杂的地址库仍然有以下问题没有解决:


1.为了可以及时失效一个城市下所有的地址,又可保障一线操作的连续性,我们为了保障数据的一致性,以及数据失效后缓存和数据库之间的同步,而采用了lua来封装了redis,形成了一个算法簇。好处是流程清晰,过程明了,但是缺点也是显而易见的。


2.通过大批量测试和验证,这样大批量的redis操作,对内存及IO占用资源较多,故而会产生很多slowlog。


3.地址库里面存储着所有用户下单的全地址,并且还有根据全地址计算出来的精简地址,用于地址的粗略匹配。而把这个运算的过程放在redis端,用lua来实现是最直接的,但是同样会带来很多的问题,数据同步延迟,算法的时间复杂度往往是o(n),地址越多问题暴露的愈加明显。


由此,有了下一版的架构升级:




这个版本中,把复杂的运算都从redis的实例中解放出来了,而采用了清晰可见的服务端每个shard实时运算模式,放弃集中式运算。同样对于缓存部分,我们也对每一个分片进一步做到了跨机房的高可用。




四.预分拣主服务进化论


1.对于预分拣系统,是根据不同的订单类型进行一系列的算法及订单属性得出最佳匹配的站点或者路区甚至配送员。那么订单类型众多,每种订单类型的处理逻辑都不尽相同,且追踪到本质都是一些基本服务的排列组合,如地址库服务、机器学习服务、配送配置服务、gis服务等。


2.而之前版本的代码中需要很多判断,如没有代码洁癖则很容易if else里嵌套多层if,再嵌套一层do while,里面继续夹带一个switch case…这样糟心的代码也是够了,可维护性和可扩展性可想而知。


3.由此产生了一个想法,可否把这些不同的订单类型和判断条件进行归类,再对基本服务进行整合,从不同中找共同点。订单类型不同,处理的方式不同,但是订单类型的判断条件不外乎存在于订单的几十个属性中,处理的方式归结下来不外乎几十种。那么以订单类型为模板,提炼和整合处理逻辑的原子服务为组件,便能很好的把组件自由的安装于模板之中,从而灵活的配置各种订单的处理逻辑了,这就提炼出了规则引擎+原子服务的架构模式。


工作流规则引擎可以用jboss的drools来实现,原子服务的组合调用用一个invoker就搞定了。


五.细节决定成败


1.日志写入造成的io不可小觑,即便异步化量很大的情况下同样会对系统性能造成很大的影响。一定严格的控制日志的级别,很多开发人员习惯性把日志级别设置为debug,根据日志作为唯一手段来做系统的异常监控。日志越少并发性能越高,磁盘写入会产生较大的io,需要结合具体场景来做平衡。


2.绝大多数的同步场景都是可以异步化的,异步化之后的好处不言而喻。尽可能把对数据库操作的,需要外部通信的,需要回传消息的,需要生成任务的,需要同步状态的服务放入异步中来处理。异步在处理时,可以很好的控制线程池的使用策略和每个线程执行的过程。


3.欧陆法系的国家一般都是有罪判定。而对于外部重要依赖,同样以默认不可用的为原则而设计架构。需要注意的是这一定是重要的外部依赖,否则系统会再多种补漏机制之上变得复杂无比。对于重要的依赖,如是数据层面的,能提前缓存的最好提前缓存,需要计算的最好分别计算好再归一统计。


4.对外提供的服务,最好做一下分组。可以同样的服务根据分组来区别不同的调用者,也可以拆分一个分组来做线上的灰度测试,甚至可以为了减轻底层数据库的压力而用分组来巧妙的散列开。对每个分组还可以单独来设置允许访问的最大并发量,以及分组的权重设置,还有服务的服务队列策略配置,如固定大小的服务量,或者带有缓冲池的服务等。


六.线上军演二三事----接口性能测试


每年两次大促之前的军演可谓热闹非凡,处处是这样的声音,“还有十分钟,快看看cpu load怎么样,看看目前线程数是多少,mq有没有积压1000以上?”。性能无小事,咱们就简单说说性能测试的事情。


1.不打无准备的仗,在双11来临之前,必定需要充分的压力测试。这会引出一片问题,性能压力要在什么样的峰值,以及承载的并发数是多少,tps如何计算呢?首先要充分了解你的调用方,他们最大的并发数是多少,调用你的接口时是否有限流措施,每个线程之间是否有执行间隔,是否会有失败重试的机制,重试的策略是什么。根据以上条件,再粗略判断一下每天高峰的持续时间,然后就能计算出tps了,这个tps是比较理想的情况,一般我们还会乘以一个系数,得出最终预期的tps。


2.正式开始压力测试了,首先注意的点是系统资源的占用情况,包括cpu、内存、磁盘占用、网络、线程数,某一项若是出现了瓶颈或者不稳定的情况,则重点去分析产生的原因,从而挖出性能薄弱点进行优化。


3.性能测试的两个重要关注点是服务的响应时间和最大tps,如果不稳定,那么一定是gc或者批量的程序再作怪。更细粒度的响应时间我们借助一些外部工具来记录具体每个函数的执行时间,开发人员可以用apache的jemter之类的工具来具体分析。


4.性能测试已经完成,并且方法的性能也达到了要求,我们的工作都做完了吗?至少有一点是必须要做的,就是对服务的限流与保护,这样可以防止调用者出现故障而疯狂调用导致服务宕机,也可以防止其他恶意的调用。


京东用户地址数量庞大,配送员妥投之后的用户属性关联也非常有价值,关于这些自然语言处理、机器学习建模、搜索优化等内容就待下回了。




幸运飞艇"互联网+"案例,请阅读原文,访问ITA1024 中国互联网+第一网站。


中国互联网+第一网站
abc.ita1024.com


中国互联网技术联盟以“技术驱动 跨界连接 合作创新”为使命,联合国内所有一线互联网公司、电商公司、技术公司、资本机构、媒体机构、政策研究机构,整合社会化资源、打造社会化组织、建立社会化服务平台。


为互联网快乐赛车和传统快乐赛车“互联网+互联网”升级转型战略提供必要的专家资源、媒体资源、资本资源、技术资源。

快乐赛车提供有价值的资讯&数据服务、跨界连接服务、“互联网+”商学院服务/技术学院服务/专家服务、媒体包装推广服务、融资对接服务。

  • CEO、CIO请访问:http://abc.ita1024.com 幸运飞艇的行业应用案例供您学习了解。

  • CTO请访问:http://www.ita1024.com 幸运飞艇的高端技术分享活动等您参加。

  • 关于ITA1024(中国互联网技术联盟):http://abc.ita1024.com/about

  • 如果希望参与互联网+快乐赛车高端访谈,或者提供线索,开展合作请发邮件至:openday@ita1024.com