淘宝购物车扩容与性能优化(下)

简介: 淘宝购物车扩容与性能优化(下)

淘宝购物车扩容与性能优化(上):https://developer.aliyun.com/article/1443492


网络包传输


 包大小对网络rt影响分析


在当今的网络环境中,上下行带宽往往是不对称的,这种不一致性在移动网络中也很常见,网络运营商为了更有效地利用有限的无线频谱资源,往往会分配更多的带宽给下行链路,以提供更好的媒体消费体验。考虑到这种上下行带宽的不一致性,上行包的优化往往比下行包的优化收益更大。因为下行通常有更多的可用带宽,即使存在一些性能瓶颈,用户也可能不会立即感受到明显的性能下降。而上行链路由于其固有的带宽限制,任何性能的微小提升都能给用户带来明显的体验改善。优化上行数据包,例如通过减少发送的数据量、实现数据包的压缩、优化传输协议以及应用智能队列管理,能够有效降低延迟、提高上传速度,从而在带宽受限的情况下尽量减少阻塞和等待时间。


上行包优化收益略大于下行包优化收益。

 购物车状态协议缓存


回到购物车的场景,上行包最直观的膨胀点就在于请接口求中params字段:

这一份长长的params里包含了,页面组件,页面状态等多个信息,其中页面组件是客户端渲染强依赖数据,而状态数据是服务端业务逻辑依赖数据。
状态数据同时存在于上下行协议中,且客户端不依赖其做逻辑处理,所以理论上这份数据可以不作为协议下发,并且在服务端的“历史长河”中,由于缺乏对该信息的管控,状态协议数据可以随意的追加,导致了这份大数据包逐渐成为上下行网络包中的一份包袱。

状态协议数据同时存在于上行与下行协议中,且客户端不依赖。


为了达到和客户端解耦的效果,最直观的方法就是将这份数据存到缓存中。然而在结合交易流量和现有计算资源做一个简单估算后,我们发现事情并没有那么简单。结合淘宝购物车的峰值流量来测算,整体产生的带宽量级会达到数十GB,在当前的规格资源下根本不够用。


  • 压缩与缓存


前文也提到了我们的终极目标是把服务端依赖数据放到缓存中,完全与客户端解耦。经过结合业务逻辑的状态协议裁剪,包大小已经有所降低,带宽瓶颈与可用性问题确实得到了缓解。那么在将这份数据存到缓存之前,还有没有可以继续“压榨”的空间呢?答案是当然是有。

base64 和 压缩算法:在原有的模式下,状态协议通过客户端和服务端直接传输来做保持,故需要通过使用base64编码确保数据能正确地存储和传输,而不是通过原始的二进制格式。这种方式会带来大约33%的数据膨胀,在确定了状态数据往缓存中存储的优化方向后,状态数据可以不再做base64编码,而转为直接存字节数组

  1. Base64编码原理:Base64编码将每组3个字节(共24位)的原始数据划分为4个单元,每个单元6位。由于每6位只能表示64种状态(2^6 = 64),Base64编码选择了一个64字符的集合来表示这些状态。这个字符集通常包括大写和小写英文字母(A-Z, a-z)、数字(0-9)、加号(+)和斜杠(/)。实际编码时,Base64处理器会查找这个字符集,将每个6位的值映射为相应的字符。
  2. 数据膨胀:考虑到大多数字符编码(如ASCII,UTF-8)中,一个字符通常占用8位(1字节)。在Base64编码中,每4个字符用来表示原始数据的3个字节。这意味着每个原始字节在编码后占用了8 * 4 / 3 = 10.666...位。换言之,原始数据的大小会增加大约1/3(33%)。

我们同时也测试了原始长度为36KB(未压缩状态数据)在各个压缩算法下的表现,虽然部分算法(例如Brotli)能实现更高的压缩比,但是综合解压和压缩时间来看,gzip仍然是最好的选型

购物车状态缓存模式:经过上述优化后,状态数据的大小在网络传输包能进一步得到缩减,从而减小带宽和存储上的压力。状态数据在客户端与服务端的交互方式也由原先网络传输的模式,改为客户端解耦的服务端缓存模式。客户端上行数据中不再持有状态数据,业务状态的计算完全交付服务端,若出现缓存超时或异常则通知客户端降级为网络传输(原有模式)。

 流式Api


经过上述的优化后,我们发现有一个遗留的“坑”点,为了能保证异常情况下用户操作能兜底上传一份状态数据,下行包中仍然保留了全量的状态协议。此时就需要我们在保证兜底能力的情况下,把下行包中的状态协议也进行剔除。

  • 流式api介绍


参考业界流式API模型,说法各不相同,主要是以下三种:

  1. request streaming: 多个上行对应一个下行;
  2. response streaming: 一个上行对应多个下行;
  3. bidirectional streaming: 端云双向流式服务;

流式API是实现 “response streaming”,即一个上行多个下行,请求模型如下:



  • 引入流式后


通过流式api的引入,下行包拆成了主包与副包,状态协议在副包中,其余数据保留在主包。客户端在收到主包后即可执行渲染逻辑,等效于状态数据在网络传输中的耗时,不再影响用户端到端体验耗时


服务端接口


 接口耗时分析


服务端耗时占比:从用户的操作数据来看,大部分用户操作商品数量较少,这些情况下服务端的性能表现尚可,这意味着服务端耗时对购物车这部分用户的影响不大。但是随着商品数量的增加,服务端耗时在端到端耗时中占比不断攀升,这意味着服务端性能对购物车的深度用户影响较大

商品数据较少时:务端优化点在于下发数据量,在网络包优化中已经得到了解决

商品数据较大时:服务端优化点在于降低耗时


 并行化改造


购物车的服务端逻辑一大特点是平均商品数较大,其中对于商品数的处理在历史上多使用简单的foreach直接遍历做计算。针对解决过多foreach逻辑最有效的方法自然是做并行化处理。购物车的流程基于业务特性将各个独立域活动进行串联,流程简化图如下:


结合业务回头来看整个流程,会发现全串行流程中低效的点:

  1. 部分节点之间并没有严格的顺序关系,例如查服务数据在注入商品标签后即可执行,且服务数据当前只在视图层构建有用。
  2. 互不依赖的下游服务例如库存查询、营销计算、查服务数据是低效的串行查询。
  3. 购物车平均商品数过大的特点,导致和商品数相关的节点内部做foreach循环非常低效,典型的有例如注入商品标签、视图层构建。


找到问题后,我们最终采用基于ForkJoinPool的方式实现购物车并行化改造:

ForkJoinPool主要针对那些可以拆分成多个子任务的大任务进行优化。它的核心技术原理是基于“工作窃取”算法,每个工作线程都有自己的任务队列,当线程完成自己队列里的任务后,它可以从其他线程的队列尾部窃取任务来继续工作,这样可以保持所有工作线程的高效运行,避免了线程闲置。ForkJoinPool 通过 ForkJoinTask 的子类如 RecursiveActionRecursiveTask 来支持任务的分割(fork)和结果的合并(join)。


这种面向多子任务且支持并行化的方式,就非常适用于购物车场景中的多商品处理,例如

  1. 并行处理多商品任务:购物车的一大特点就是平均商品数多,每个商品标签注入、单个视图分组构建等操作都可以作为一个子任务独立执行。
  2. 任务可拆分性:购物车中的每个商品处理逻辑都是独立的,这使得任务可以很方便地被拆分成小任务并行执行。
  3. 提高响应性能:使用 ForkJoinPool 可以在多核处理器上同时执行多个商品的处理任务,从而减少总体的处理时间,提高系统的响应速度。
  4. 动态任务调度:"工作窃取"算法能够在运行时动态地重新平衡任务负载,如果某些商品的处理耗时较长,ForkJoinPool 能够确保其他线程不会闲置,而是帮助处理剩余的工作。


结合ForkJoinPool的方式做并行优化后的购物车流程简化图如下:


  1. 注入商品标签和视图层构建,从商品维度拆分子任务,使用并行化。
  2. 将部分仅视图层且互不依赖的下游数据节点做聚合,内部做并行查询。


团队介绍

看到这里相信诸位能够感受到购物车技术体系的复杂度和深度,笔者所在的团队是淘天集团交易前链路技术团队(购物车&下单),在这里,你能够不断被各种商业模式烧脑,也能够不断被各种新兴技术锤炼,更能收获一群志同道合的战友。值此变革关键时期,也急需有能力和有梦想的你一起参与:

1. 负责淘宝购物车、下单等面向全民用户的C端产品演进和迭代,每一次需求每一行代码都能创造巨大的商业价值。

2. 支撑集团16N组织下形态各异电商的购物车、下单平台应用的维护(buy2 carts2),在这里见证如何针对形态各异的电商进行架构抽象出电商内核,并通过高度灵活的开放扩展机制解决业务的差异性。

3. 操刀完整的端到端协议的设计、演进和优化(奥创),见证移动时代在客户端不发版的情况下,如何既能高效满足产品需求迭代,又能获得native一样优异的消费者体验。

4. 全程保障每一次大促流量洪峰背后的业务安全和稳定性,全力促成持续的平台架构演进,确保用户每一次购物车浏览,每一次下单能够丝滑顺畅。

如果您有兴趣,可以点击下面的链接或者通过邮箱bonan.hbn@alibaba-inc.com与我们联系和交流,期待您的加入。https://talent.taotian.com/off-campus/position-detail?lang=zh&positionId=1089401

目录
相关文章
|
4月前
|
消息中间件 算法 数据库
架构设计篇问题之商城系统高并发写的问题如何解决
架构设计篇问题之商城系统高并发写的问题如何解决
|
6月前
|
存储 NoSQL 数据库
为什么要用 Tair 来服务低延时场景 - 从购物车升级说起
“购物车升级”是今年双十一期间提升用户体验的关键项目,展示了大淘宝技术团队致力于通过技术突破消费者和商家体验的天花板。低延迟是这些挑战中的核心,内存数据库Tair因其高吞吐、大连接数、热点请求处理、异常流量管理和复杂计算逻辑优化等特点,在低延迟场景下表现出色。Tair使用内存/SCM混合存储和各种索引来提供低延迟服务,并通过无锁并发、水平扩展分区等技术应对高并发。此外,Tair还通过热点策略、流控和执行流程优化等手段确保在大促时的稳定性和性能。Tair在双十一期间支持了购物车、销量统计、卖家优惠券召回和互动场景等多种业务,展现其低延迟和高并发的能力。
77134 11
|
6月前
|
监控 NoSQL Java
记一次线上商城系统高并发的优化
记一次线上商城系统高并发的优化
146 0
|
6月前
|
SQL 缓存 数据库
淘宝购物车扩容与性能优化(上)
淘宝购物车扩容与性能优化(上)
265 2
|
消息中间件 负载均衡 应用服务中间件
秒杀场景的设计思路和方案
秒杀场景的设计思路和方案
331 0
|
SQL 存储 缓存
由浅入深的介绍扣减业务中的一些高并发构建方案(上)
在后台开发领域,高并发的扣减一直是比较热门的话题,在各类技术博客、大会分享以及面试问题中出现频率都非常高。可见它的重要性和技术知识点的密集性。
由浅入深的介绍扣减业务中的一些高并发构建方案(上)
|
存储 SQL 缓存
由浅入深的介绍扣减业务中的一些高并发构建方案(中)
这一讲我将由浅入深的介绍如何基于缓存来实现单机万级这些并发扣减目标。
|
监控 NoSQL Dubbo
从一个电商平台的库存同步谈性能优化和方案落地
从一个电商平台的库存同步谈性能优化和方案落地
438 0
从一个电商平台的库存同步谈性能优化和方案落地
|
canal 存储 缓存
【架构】千万级购物车系统缓存架构方案
本文主要介绍redis在千万级系统中设计架构方案,包括主架构设计、缓存一致性方案、大value处理方案和redis限流和故障恢复降级
【架构】千万级购物车系统缓存架构方案
|
存储 canal 缓存
【高并发项目实战】千万级并发的购物车系统设计与代码详解
本文主要介绍redis在千万级系统中设计架构方案,如何在设计上抛弃mysql,主架构设计、缓存一致性方案、大value处理方案和redis限流和故障恢复降级方案设计与代码详解