年度案例双十一大型电商统一服务架构实战

发布时间:2024-06-07 点击:139
京东商品详情页技术方案在之前这篇文章已经为大家揭秘了,接下来为大家揭秘下双十一抗下几十亿流量的商品详情页统一服务架构。
这次双十一整个商品详情页在流量增长数倍的情况下,没有出现不可用的情况,服务非常稳定。
统一服务提供了:促销和广告词合并服务、库存状态/配送至服务、延保服务、试用服务、推荐服务、图书相关服务、详情页优惠券服务、今日抄底服务等服务支持。
其实就是一个大的网关,提供各种服务。但区别于一般网关,它还提供业务逻辑的处理。这些服务中有我们自己做的服务实现,而有些是简单做下代理或者接口做了合并输出到页面,我们聚合这些服务到一个系统的目的是打造服务闭环,优化现有服务。并为未来需求做准备,跟着自己的方向走,而不被别人乱了我们的方向。这样一起尽在自己的掌控之中,想怎么玩就看心情了。
大家在页面中看到的c.3.cn/c0.3.cn/c1.3.cn/cd.jd.com请求都是统一服务的入口。我们分别为有状态服务和无状态服务提供了不同的域名,而且域名还做了分区。
为什么需要统一服务?
商品详情页虽然只有一个页面,但是依赖的服务众多,我们需要把控好入口,一统化管理。
这样的好处:
统一管理和监控,出问题可以统一降级
可以把一些相关接口合并输出,减少页面的异步加载请求
一些前端逻辑后移到服务端,前端只做展示,不进行逻辑处理。
有了它,所有入口都在我们服务中,我们可以更好的监控和思考我们页面的服务,让我们能运筹于帷幄之中,决胜于千里之外。
在设计一个高度灵活的系统时,要想着当出现问题时怎么办:是否可降级、不可降级怎么处理、是否会发送滚雪球问题、如何快速响应异常。完成了系统核心逻辑只是保证服务能工作,服务如何更好更有效或者在异常情况下能正常工作也是我们要深入思考和解决的问题。
整体架构
整体流程
请求首先进入nginx,nginx调用lua进行一些前置逻辑处理,如果前置逻辑不合法直接返回;然后查询本地缓存,如果命中直接返回数据。
如果本地缓存不命中数据,则查询分布式redis集群,如果命中数据,则直接返回。
如果分布式redis集群不命中,则会调用tomcat进行回源处理;然后把结果异步写入redis集群,并返回。
如上是整个逻辑流程,可以看到我们在nginx这一层做了很多前置逻辑处理,以此来减少后端压力,另外我们redis集群分机房部署,如下图所示:
即数据会写一个主集群,然后通过主从方式把数据复制到其他机房,而各个机房读自己的集群;此处没有在各个机房做一套独立的集群来保证机房之间没有交叉访问,这样做的目的是保证数据一致性。
在这套新架构中,我们可以看到nginx lua已经是我们应用的一部分,我们在实际使用中,也是把它做为项目开发,做为应用进行部署。
我们主要遵循如下几个原则设计系统架构:
两种读服务架构模式
本地缓存
多级缓存
统一入口/服务闭环
引入接入层
前端业务逻辑后置
前端接口服务端聚合
服务隔离
两种读服务架构模式
读取分布式redis数据架构
可以看到nginx应用和redis单独部署,这种方式是一般应用的部署模式,也是我们统一服务的部署模式,此处会存在跨机器、跨交换机或跨机柜读取redis缓存的情况,但是不存在跨机房情况,因为使用容器化,不太好做不跨机柜的应用了。通过主从把数据复制到各个机房。如果对性能要求不是非常苛刻,可以考虑这种架构,比较容易维护。
读取本地redis数据架构
可以看到nginx应用和redis集群部署在同一台机器,这样好处可以消除跨机器、跨交换机或跨机柜,甚至跨机房调用。如果本地redis集群不命中, 还是回源到tomcat集群进行取数据。此种方式可能受限于tcp连接数,可以考虑使用unix domain socket套接字减少本机tcp连接数。如果单机内存成为瓶颈(比如单机内存最大256gb),就需要路由机制来进行sharding,比如按照商品尾号sharding,redis集群一般采用树状结构挂主从部署。
本地缓存
我们把nginx作为应用部署,因此大量使用nginx共享字典作为本地缓存,nginx lua架构中,使用httpluamodule模块的shared dict做本地缓存( reload不丢失)或内存级proxy cache,提升缓存带来的性能并减少带宽消耗。
之前的详情页架构也是采用这种缓存。另外使用一致性哈希(如商品编号/分类)做负载均衡内部对url重写提升命中率。
在缓存数据时采用了维度化存储缓存数据,增量获取失效缓存数据(比如10个数据,3个没命中本地缓存,只需要取这3个即可)。维度如商家信息、店铺信息、商家评分、店铺头、品牌信息、分类信息等;比如本地缓存30分钟,调用量减少差不多3倍。
另外使用一致性哈希 本地缓存,如库存数据缓存5秒,平常命中率:本地缓存25%%uff1b分布式redis28%%uff1b回源47%%u3002一次普通秒杀活动命中率:本地缓存 58%%uff1b分布式redis 15%%uff1b回源27%%u3002而某个服务使用一致哈希后命中率提升10%%u3002对url按照规则重写作为缓存key,去随机,即页面url不管怎么变都不要让它成为缓存不命中的因素。
多级缓存
对于读服务,在设计时会使用多级缓存来尽量减少后端服务压力,在统一服务系统中,设计了四级缓存,如下图所示:
首先在接入层,会使用nginx本地缓存,这种前端缓存主要目的是抗热点;根据场景来设置缓存时间。
如果nginx本地缓存不命中,接着会读取各个机房的分布式从redis缓存集群,该缓存主要是保存大量离散数据,抗大规模离散请求,比如使用一致性哈希来构建redis集群,即使其中的某台机器出问题,也不会出现雪崩的情况。
如果从redis集群不命中,nginx会回源到tomcat;tomcat首先读取本地堆缓存,这个主要用来支持在一个请求中多次读取一个数据或者该数据相关的数据。而其他情况命中率是非常低的,或者缓存一些规模比较小但用的非常频繁的数据,如分类,品牌数据;堆缓存时间设置为redis缓存时间的一半。
如果java堆缓存不命中,会读取主redis集群,正常情况该缓存命中率非常低,不到5%%u3002读取该缓存的目的是防止前端缓存失效之后的大量请求的涌入,导致后端服务压力太大而雪崩。默认开启了该缓存,虽然增加了几毫秒的响应时间,但是加厚了防护盾,使服务更稳当可靠。此处可以做下改善,比如设置一个阀值,超过这个阀值才读取主redis集群,比如guava就有ratelimiter api来实现。
统一入口/服务闭环
在《亿级商品详情页架构演进技术解密》中已经讲过了数据异构闭环的收益,在统一服务中也遵循这个设计原则,此处主要做了两件事情
数据异构,如判断库存状态依赖的套装、配件关系进行了异构,未来可以对商家运费等数据进行异构,减少接口依赖。
服务闭环,所有单品页上用到的核心接口都接入统一服务。
有些是查库/缓存然后做一些业务逻辑,有些是http接口调用然后进行简单的数据逻辑处理;还有一些就是做了下简单的代理,并监控接口服务质量。
引入nginx接入层
在设计系统时需要把一些逻辑尽可能前置以此来减轻后端核心逻辑的压力。另外如服务升级/服务降级能非常方便的进行切换,在接入层做了如下事情:
数据校验/过滤逻辑前置、缓存前置、业务逻辑前置
降级开关前置
ab测试
灰度发布/流量切换
监控服务质量
限流
服务有两种类型的接口:一种是用户无关的接口,另一种则是用户相关的接口。因此使用了两种类型的域名c.3.cn/c0.3.cn/c1.3.cn和cd.jd.com。
当请求cd.jd.com会带着用户cookie信息到服务端。在服务器上会进行请求头的处理,用户无关的所有数据通过参数传递,在接入层会丢弃所有的请求头(保留gzip相关的头)。
而用户相关的会从cookie中解出用户信息然后通过参数传递到后端;也就是后端应用从来就不关心请求头及cookie信息,所有信息通过参数传递。
请求进入接入层后,会对参数进行校验,如果参数校验不合法直接拒绝这次请求。对每个请求的参数进行了最严格的数据校验处理,保证数据的有效性。
如图所示,我们对关键参数进行了过滤,如果这些参数不合法就直接拒绝请求。另外还会对请求的参数进行过滤然后重新按照固定的模式重新拼装url调度到后端应用。此时url上的参数是固定的而且是有序的,可以按照url进行缓存
缓存前置
很多缓存都前置到了接入层,来进行热点数据的削峰,而且配合一致性哈希可能提升缓存的命中率。在缓存时按照业务来设置缓存池,减少相互之间的影响和提升并发,使用lua读取共享字典来实现本地缓存。
业务逻辑前置
在接入层直接实现了一些业务逻辑,原因是当在高峰时出问题,可以在这一层做一些逻辑升级。
后端是java应用,当修复逻辑时需要上线,而一次上线可能花费数十秒时间启动应用。重启应用后

营销网站建设工作中,需要了解的十大SEO技巧
【黄山seo】了解查找引擎优化的营销方式
低延时、稳定、极具性价比的香港服务器
如何把网站迁移到云服务器
电脑版uc浏览器打开网页不显示图片如何解决
阿里云服务器什么时候最便宜
带宽和延迟是怎样影响您的香港服务器的呢?
哪里有便宜的云服务器