当前位置: 首页 > 科技观察

京东价格保护高并发 - 七步走保证用户体验

时间:2023-03-14 18:12:51 科技观察

京东价格保护高并发|七步保障用户体验在工作上,京东推出了一项特色服务——价格保护。促销活动正式开始后,不少用户开启了价格保护。在这种高并发的情况下,如何保证用户体验,如何保证系统的稳定性、高可用、快速的计算结果,是本文关注的重点。我们将按照下图来分享做法:1.筑高墙对于任何一个网站,我们的系统都需要采取保护措施,以保证系统在面对海量流量时不至于不堪重负;全面保护系统。从上图可以看出,我们在不影响用户体验的情况下,对正常用户和暴力用户采取了降级、限流等措施,以保证系统的稳定性。那么具体怎么做呢,我们分别说一下限流和降级。1.限流(1)普通用户限流当普通用户访问系统时,超过了系统的承载能力。这时候就需要限流,防止系统不堪重负,不可用。通过压测得到单机的最大承载能力,然后通过限流计数的方式在单台服务器上统计访问次数,并且在一段时间内只能设置N次访问.比如设置1w/min,当1分钟内达到阈值,就会进入降级配置。过了这个时间段,2分钟后会重新开始计数,以保证单机不会超过**承载能力,后续的每台服务器都会按照这个阈值进行配置。(2)暴力用户限流暴力用户频繁刷应用系统,我们需要在这一层做一些防刷,比如清洗恶意流量,做一些黑名单。当有恶意流量时,通过限制IP和用户的方式将其排除在系统之外,防止这些恶意流量淹没系统。这里使用rediscount按照IP或者用户维度自动加1,限制为120/分钟,防止恶意流量影响我们正常的用户访问。2.降级当某个接口出现问题时,我们可以降级该接口并快速返回结果,不会影响主进程。那么降级是如何进行的呢?由于我们有一个分布式集群和大量的应用服务器,所以我们需要集中管理降级开关。这里我们做了一个统一配置的switch组件,通过zookeeper将配置推送到各个server节点。同时在zookeeper和应用服务器上分别会有快照数据,这样如果统一配置开关组件出现问题,我们的应用也会读取本地的快照数据,不会影响应用本身。同时,当应用重启时,我们也会通过接口拉取配置中心上的最新快照。对于降级,我们还需要友好的提示。如果前端降级了,我们需要友好提示,或者显示降级页面,尽量不影响用户体验。2.光量良对于大型并发网站,我们需要准备各种数据,区分动态资源和静态资源,缓存静态资源以应对瞬时流量。1、CDN页面上的静态资源,比如js、css、图片、静态html等资源,可以提前准备好放在CDN上。当页面被请求时,这部分网络请求可以发送到CDN网络,减少连接请求。减轻应用服务器压力。在使用CDN时需要注意的是,当网页和js发生变化时,无论是先部署web应用还是先推送js到CDN,都可能会出现js脚本错误。因此,我们需要在网页上切换CDN。首先,将资源访问切换到web机器。在线验证无问题后,再部署CDN,将静态资源访问切换到CDN。2、数据缓存我们在获取数据的时候,首先要判断一下,哪些地方可以使用缓存,哪些地方需要去读数据库。应主动缓存属性固定、访问频繁的动态资源。比如下单时的快照,订单的种类,下单的时间,订单中的商品,商品的下单价格等等,都是固定的。我们收到订单消息主动缓存数据,以便后续显示订单中的商品价格和计算是在下单价格和申请价格保护时的促销价时准备的,无需实时访问订单界面,这减轻后台接口压力,加快采集速度。3、精简化繁在高并发的情况下,需要快速响应。当请求过程中获取的数据过多时,可能会降低响应速度。因此,应简化加工,只采用黄金工艺。1.前端简化。用户访问页面时,只关心数据的关键部分,所以我们需要先获取主要数据,立即返回页面,页面通过ajax加载分支数据,实现页面完整性。这样既保证了用户体验,又提高了系统的响应能力。图-价格保护申请以价格保护申请页面为例。当用户进入页面时,他们想要保护产品价格。因此,产品列表和应用按钮是用户最想看到的。其他信息,如商品的最新保价记录、订单价格等数据,可稍后加载。2.简化后台用户申请价格保护时,由于处理逻辑复杂,需要与20多个系统交互计算结果,所以我们采用异步处理方案。那么,在访问应用时,任何系统都可以通过三步访问该应用:插入防重存储应用数据和发送处理任务,保证用户的应用可以快速访问,提高系统的接单能力,以及然后在执行后续处理任务时加速,在不影响用户体验的情况下,可以快速返回结果。后续“无限处理,快速解决”章节将详细讲解如何以最快的速度处理任务。4、合二为一在高并发请求的情况下,由于请求量巨大,CPU会频繁切换上下文,导致CPU占用率飙升,性能下降。所以,我们需要尽量减少请求的数量,能合并的就合并。还以上述“图片保价申请”为例。由于订单中的商品价格已经缓存在后台,我们可以根据订单的维度合并商品价格,通过ajax请求就可以获取同一个订单下所有商品的价格.刷新是否满足价格保证请求合并。无论用户点击应用多少次,结果都通过ajax合并刷新,减少了请求后端的连接访问。五、分而治之1、对于前端网站,我们按照访问来源和主次进程划分集群:目前很多网站都生产了移动端和PC端,所以我们也按照访问来源来划分应用集群。这样既可以保证源集群之间互不影响,又可以根据不同访问源的流量合理分配机器。同时,我们还按照主次服务进行集群区分,将不重要的服务放在非主服务集群上,不影响主要业务流程。如“图片-保价申请”所示,价格、最新访问记录、申请结果刷新这三个功能并不是主要的业务流程。如果是在非主业集群上接入,即使非主业集群出现问题,也不会影响价格保护金流程。2、后端数据后端读写分离,分库分表:查询数据时,是否需要实时数据决定是否使用read-from-database。在写入大量数据时,应根据业务需要的维度对数据进行分库分表,以减轻数据库的压力。下面我们说说我们是如何分库的。价格保护系统的主要维度是用户,所以我们根据用户的PIN进行分库路由,利用PIN获取Hash值,然后取模。比如我们要分成两个库,算法哈希值为%2。那么问题来了,当业务量开始增长的时候,两个库不能满足我们的要求,需要扩建更多的库,比如五个库,怎么办?一般的方法是清理两个库的数据,然后按照新的数据库数量为5重新分配数据,hash值为%5。这样做太麻烦了,所以我们这里使用二叉树算法,可以平滑的扩容数据库,不需要打散和重新分配数据。→2→4→8,新裂变的节点只需要将数据冗余父节点按照2的N次方向下裂变即可。然后看如何扩容:扩容前有2个数据库DB-0和DB-1,现在我们需要扩展到8个数据库,以DB-0为例:我们只需要找到3个新的数据库,作为从库挂载到DB-0上,然后进行主从复制;在数据量最少的时间段,切断主从复制,同时将扩容的ABC三个从库切换到主库,此时4个数据库数据一致,各有1个/4的数据属于自己,其他数据是冗余数据。将路由算法调整为hash值%8,部署新应用,连接所有主要数据库进行访问。这个时候同时有新旧两个应用。但是,如果旧应用接管,将无法与新裂变的数据库2、4、6同步;创建数据迁移任务和数据比对任务,从0库主从复制中断时刻开始,根据哈希值%8,同步2、4、6的数据(以finalstate)到各自的数据库,同时做数据比对和校验;停止旧应用程序,并开始从扩展的新应用程序中接管所有数据。此时,数据库扩容完成。扩容完成后,我们只需要清理冗余数据即可。有很多方法可以实现它。例如,我们可以使用数据归档任务:写入一定时间前的防重数据进行归档。这样,经过一段时间后,冗余的数据就会消失会被清理干净,而且因为有防重复,不会因为多次归档而导致归档数据重复。6.无限处理经过以上步骤,用户可以正常打开页面,提交商品价格保护申请,那么如何吸纳所有庞大的申请并快速返回,就成了我们系统的一大难题。如果处理速度慢,有可能获取到当时不准确的促销价格,导致用户价格保护失效,用户体验急剧下降。下面我们将演示如何从有限到无限:图-有一个限制。大家看看,为什么上图中有一个限制呢?从应用存储到处理应用任务,都使用了业务DB集群。在这种情况下,如果每分钟能够接受100万个订单,那么处理能力就只有每分钟20万个。这个时候数据库已经达到了瓶颈,所以想要处理的更快,只能继续做分库,增加业务WK集群机器,这样也可以提高处理能力,但是会造成巨大的浪费接单能力。通过这些,大家肯定已经猜到了,是的,我们可以通过将两个集群的DB分开来进行订单接收和任务处理来解决这个问题,并且不会互相影响。怎么做?请看下图:我们的业务订单集群只做业务处理,保存到业务DB集群中。通过业务WK集群,将任务发送给JMQ中间件,任务进程处理SV集群进行消息监听。消息分库插入到流程处理DB中,每个流程处理DB会对应一组任务处理WK集群,所以按照上面每分钟20万条计算,我们这里只需要5组。这样无论业务应用有多大,我们的任务处理都可以随时扩展。7、快速解决在上面的“无限处理”中,我们可以随时展开,那么如何才能实现最快的任务处理呢?这一节,我们主要讲一下如何让任务处理的最快,同时,在出现异常的情况下,任务不丢失。由于价格保证申请的处理,业务非常复杂。我们这里采用工作流模式,用任务节点程序全自动处理。且看任务系统如何进化,最终立竿见影。工作流流程介绍:通过工作流流程模板Template,一个应用Apply生成一个流程实例Order,每个流程实例Order都会在Order下有N个节点任务。最后阶段根据Template维度,定时获取一定数量的Task,循环执行。从机器完全执行任务的角度来看,这时候一台机器就够了,两台机器可能抓到同一个任务,造成资源浪费。第二阶段数据分块:将一批数据按照预先设定的进行分块,然后对分块数据进行区别对待。如上图所示,任务节点Task被分块。这时定时获取的Task维度发生变化,可以从Template和block两个维度获取Task。目前分为2块,所以模板可以在两台机器上执行;blocknumber越多,模板执行的机器就越多。但是我们发现最小的粒度是Task,那为什么会有Template这个维度呢?第三阶段去掉Template维度,使用最小粒度的Task。上图中使用的任务框架,是我们自主研发的。如果不用这个框架,只要保证最小粒度是Task,也是可行的。我们使用Template+TaskCode为Task生成任务代码,然后分块实现最小粒度:任务代码+块。如上图所示,每个任务仍然分为2个块??。此时3个任务分为2块,一共有6台服务器执行任务。这时候速度已经很快了,按照最小粒度来区分,但是机器数量还是有限制的,所以我们只能增加块数,让更多的机器执行。第四阶段,当任务节点产生时,将节点信息发送到消息队列,由消息驱动,让所有机器直接执行,速度提升到最快。这时候只需要保证任务内部处理速度足够快即可。能。在这个阶段,当任务执行异常,消息丢失的时候,我们还有一个第三阶段的方案来保证底线和重试,同时也保证任务能够高效的执行。夏庆峰:逆向流程技术专家,疑难杂症终结者,2014年加入京东,负责京东财务退款和保价研发建设,擅长京东逆向流程场景、金额拆分计算、下线网站优化高并发。【本文来自专栏作者张凯涛微信公众号(凯涛的博客)公众号id:kaitao-1234567】点此查看作者更多好文