苏宁“砍价团”高可用、高并发架构实践

2020 年 4 月 23 日

苏宁“砍价团”高可用、高并发架构实践

苏宁拼购 808 的火爆见证了砍价团的成功,作为一种新兴的购物营销玩法,砍价团展现出了巨大的商业潜力。不同于传统购物流程的单一模式,砍价团凝练了购物玩法和社群营销的精髓。

拼购砍价团平台化转型的战略契机

传统购物模式的劣势在于购物体验单一,更多的都只是选品→下单→支付的标准化流程, 另一方面则在于无法打通购物与社交的壁垒,实现两者的巧妙融合。

而砍价团模式则在这两个方向施以重彩:

  • 通过分享,帮砍流程使得购物充满趣味性和互动性,优化用户体验的同时也增加了用户粘性。
  • 借助于微信庞大的用户基数实现了产品在熟人社群中的快速传播,达成了良好的营销效果。

而熟人社群的传播也有利于精准遴选出更多对标用户,充分挖掘用户价值,正是这两把利刃,造就了砍价团模式的成功。

苏宁拼购 808 的成功同时也成为了进一步摸索砍价团发展模式的契机。

当前的砍价团仍然从属于拼购玩法,能够参与砍价的商品也局限于苏宁拼购的部分商品,更多的聚集在日用,快消等低成本商品,3C,家电等相对较少。

此外,砍价团玩法也可以与苏宁 O2O 模式深度嫁接,不仅仅局限于线上商品,同时也可以将线下门店纳入到砍价团版图之中,充分发挥苏宁的供应链优势,甚至连同置业,文创,乃至虚拟商品都可以和砍价团玩法深度融合。

线上与线下的齐头并进势必能为砍价团带来新一轮的爆发式增长。

笔者认为,砍价团的铺展离不开四个要素:

  • 足够有趣的玩法
  • 精准的目标用户群筛选
  • 良好的社交传播渠道
  • 优质的商品供应链

一方面可以通过主站引流,微信分享的方式实现用户群的拓展,另一方面砍价团也应该在选品方面进一步拓宽范围,给予用户更多的可选择性。

如果仅仅局限于苏宁拼购本身,那么除了商品范围的局限外,也无法吸引更多的优质商家入驻。

因此,砍价团亟待完成从玩法到通用性平台的战略转型,砍价团平台化战略,便由此应运而生。

砍价团平台业务架构设计

从一种玩法到一个平台,这种从单元到体系的跃迁背后是一种战略思路的成型。

砍价团平台本身带有一定的商业试探性质,一旦这种模式确定可行,便可迅速推而广之。除了砍价团之外,也可以吸收更多的优秀营销玩法。

这就要求砍价团平台应当具备通用性。既然面向苏宁的全产业提供服务,那么就势必要兼收并蓄,不仅仅是主站商品,线下门店商品,同时也要考虑置业,文创等产业模块。

在砍价团平台的业务设计上,要充分考虑不同类型商品的统一管理,同时也要为其他可能的营销玩法预留空间,而不仅仅是局限于砍价团。

通过实现高度的可配置与定制化,进而转型成为为全产业提供玩法定制服务的玩法平台。

在业务架构上,砍价团平台不能仅仅是复刻拼购原有的业务逻辑,而应当更具兼容性地做出相应业务架构改造。

比如对于团模式的设计而言,砍价团实质上并非传统的开团——参团模式,而更类似于单买模式,只不过引入了分享和帮砍流程。

因此对于老的成团模式,就需要废弃诸如“团满校验”,“参团流程”等冗余逻辑,而只需要记录帮砍人数与帮砍金额等信息即可。

而在活动设计层面,考虑到未来可能引入新的玩法,也应当在设计上具备充分的拓展性。

除了保留诸如活动编码,活动类型,起止时间等基本信息字段之外,也需要考虑到通过预留字段,拆分基本信息表和扩展信息表等方式为新玩法的引入留下空间。

就砍价团平台的设计初衷而言,其旨在于提供一套能够兼容各种购物玩法的中台式服务。

因而在玩法设计上应当提供具备通用性的接入模板,由业务方自由定制自身的玩法模式。这就需要对各种玩法所具备的共性要素进行精准抽绎,实现高度可配。

就前端而言,因为不同的玩法模式在用户侧展示的内容也有所差别,前端设计就需要足够灵活。

前端在开发过程中,应当在满足业务功能的基础上尽可能制定统一的开发标准,对于复用性较高的模块封装为组件,便于实现前端页面的可定制化。

就服务端而言,在服务组件的设计上要对业务模块进行细致拆分,避免业务之间的过度耦合,为新模块的引入,新玩法的配置留下空间。

在功能层面,要尽可能实现业务组件的原子性,除非涉及核心业务模块,否则不同功能之间应当尽可能解耦,便于功能的横向拓展。

比如对于活动阀值,玩法规则等一些通用性要素,其应该尽可能避免从代码层面去实现,而是通过苏宁统一配置平台加以配置。

当承接新的玩法需求时,只需要在统一配置平台进行相应的配置即可,而无需进行代码层面的改动。

百尺之台,不可止日毕功,砍价团平台化战略只能在试探和摸索中徐徐图之。不畏浮云遮望眼,风物长宜放眼量。试看前路,虽有坎坷,却亦是坦途。

砍价团平台技术架构设计

砍价团平台的架构纵向拆分

图 1:砍价团平台架构纵向拆分

在砍价团平台系统设计上,首先需要在业务层面进行逻辑解耦,砍价团平台(Bargain System,简称 BGS)在业务上可以主要拆分为四大模块,分别部署在不同的服务器集群上,不同的集群之间共享服务层的原子服务,并通过分布式远程调用框架协同工作。

①前台用户模块:前台业务模块,主要负责处理来自用户的业务请求,负荷最重同时也是最为核心的业务模块。

在硬件配置上要充分考虑本模块的机器配置能否应对庞大的的流量载荷,同时应当着重考虑本模块的容灾能力设计。

本模块主要处理活动,砍价玩法,物品,交易等服务,每种服务都由粒度更小的组件来实现,如砍价玩法下面又包含了规则,风控,金额,明细等专门业务组件。

②前台服务模块:前台服务框架层业务模块,本模块在业务上与前台用户层重合,区别在于本模块完全由服务框架层模块调度实现,主要负责分流一部分来自内网其他系统的访问请求。

如此设计的优势在于能够从流量层面对内外网的访问进行解耦,隔离部署不仅更便于精准配置机器资源,同时也能有效提高前台模块的抗风险能力,便于损害管控。

③后台模块:后台业务模块,本模块主要用于运维管理人员的日常数据维护,除了对活动信息的管理外,本模块还可以实现对玩法,规则等的高度可配置化,以便兼容更为丰富的购物玩法。

④中台模块:中台定时任务模块,本模块主要负责处理来自统一调度平台的定时任务调度请求,如过期团处理,过期订单处理等,实现对数据层的定时维护,保持数据时效性,避免产生数据冗余。

砍价团平台的架构横向拆分

图 2:砍价团平台架构横向拆分

①网络层:为了应对来自用户的巨大流量压力,苏宁在网络层采取了缓存设计,来自用户的静态资源访问请求一部分会由全局负载均衡器直接响应,以此减轻后端服务器的负载。

同时由于 BGS 系统采用双机房部署策略,因此回源请求会根据特定分拨策略被调度到两个不同的机房。

②负载层:对于进入后端服务器的请求,首先会由苏宁应用防火墙进行初步甄别。

防火墙除了负责对外网攻击,非法请求,垃圾请求进行拦截过滤之外,同时也负责在某些高并发场景下。

当流量超过阀值时进行流量控制,减轻后端服务器的压力,防止服务器引流量过高而宕机。

③应用层:穿过防火墙的请求由后端负载集群进一步分发到应用服务器进行相关的业务处理和落库操作,并响应给用户。

砍价团平台的数据层设计

图 3:砍价团平台的数据层设计

在数据层的设计上,考虑到 BGS 可能面对的巨大业务量,采取了数据库分库中间件技术。对一些业务量较大的表进行分库分表部署,进而提高数据读写效率。

比如对于团信息相关表,通过特定的算法将数据均匀铺展在多个分表中,而分表根据特定的取模算法分别部署在四个分库中。

分库中间件会根据访问请求的分片字段将请求分发到相应的分库,而无需代码层面的额外改动。

对于不带分片字段的请求,分库中间件可能需要对分库进行遍历,从而降低查询效率,因此 BGS 系统采取了额外的整库设计。

四个分库的数据通过数据迁移同步到单个整库中,对于不带分片字段或其他有特殊业务要求的查询请求,不经过中间件直接调度到整库,从而提高查询效率。

此外额外整库设计也可以在中间件与分库出现问题时承接数据库降级操作,保证业务的持续可用性。

由于数据迁移存在一定的同步延时,因此整库更适合处理对时效性要求较低的查询请求,对于写库请求和高时效性查询请求,则需要对业务进行进一步的评估。

此外, 中间件 + 数据库的设计也更加易于后期数据库的横向扩展,避免系统因为数据层的瓶颈而限制整个系统的并发处理能力。

砍价团平台的缓存设计

由于 BGS 系统可能面临的高并发情况,因此在数据层面除了中间件 + 数据库的设计之外,同时采用了 Redis 缓存来预处理一部分读写请求,减少对数据库的访问。

图 4:砍价团平台缓存设计

以库存扣减场景为例,对于一些热销商品,或者活动抢购商品,可能会在短时间内面临极高的并发压力。

因此有必要设计预操作步骤,一方面缓冲到数据库的直接压力,另一方面保证缓存与 DB 的数据一致性。

当用户下单或支付成功后,优先操作 Redis 中的数据。Redis 中的数据定时同步给 DB,同步周期可以根据业务需求进行配置。

对 DB 和 Redis 的操作放置在分布式一致性框架中,当某一步骤失败时进行回滚,避免数据不一致的情况出现。

同时 Redis 和 Sentinel 组成高可用集群,在某台 Redis 宕机时自动实现主从切换,保证缓存服务的高可用性,防止大量缓存穿透引发数据库雪崩效应。

但是这种设计有一种特殊的场景需要加以考虑,那就是在涉及诸如抢购,秒杀等高并发场景时,对于 Redis 中一些时效性较高的数据(如库存),如果其过期时间较短,可能存在过期之前最后一个同步周期内的数据在没有同步到数据库之前就失效的情况,进而造成部分数据缓存和 DB 不一致,引发活动超卖。

对于这种特殊场景存在两种技术解决方案:

  • 缩短数据同步周期,减少数据不一致状况的发生,但是这种方案会使得数据库压力增大,同时也无法完全解决数据不一致的情况。
  • 预先设置足够的缓存失效时间,杜绝在活动有效期内数据失效,虽然这种方案会增大对 Redis 空间的占用,但是在技术层面风险更小,更适合应对风险较高的场景。

高可用系统的锻造及大促保障

系统的架构设计无外乎满足两方面的要求:

  • 尽可能满足系统所承接的业务需求
  • 尽可能提高系统的抗压能力

砍价团平台作为核心系统之一,良好的抗压能力是必不可少的,这就为 BGS 系统在整体设计层面提出了更高的要求。

在实现系统的高可用性层面,BGS 系统采用了以下几种应对策略:

双机房多活部署

对于业务价值较高的系统,多活部署几乎是一个必然的选择。BGS 系统采用双机房部署策略,对用户请求进行分流,规避单点部署策略在意外因素下宕机的风险,保证业务的持续可用性。

在网络层,由全局负载均衡器对用户请求按一定的分片规则进行划拨,调度到 A,B 两个机房;在负载层,会根据相应的分拨策略对流量进行二次划拨。

当网络层与负载层的流量切分策略一致时,则不会进行额外的流量划拨;当不一致时,负载层会进行补偿性的流量二次拨分,最终实际的流量调拨情况将遵循负载层的拨分策略。

负载层的流量调度以系统维度进行切分,不同系统可根据实际情况配置分拨策略。

除了对流量进行补偿,负载层还可以承担在网络层调拨策略失效后的替代性方案,保证不会因为网络层面的调拨失效而导致单机房负载压力过大。

在应用层,对于系统之间的调用请求,则集中在主机房进行处理。除此之外,鉴于数据层面的强一致性要求,系统间调用可以进行接口级别的策略控制,来对一些写操作进行单机房调度,确保数据的单机房写库。

在数据层面,为了保证数据的强一致性,采用竞争型策略,以保证主备库数据一致性。

主库写入的数据会同步迁移到备库进行温备,以确保主机房宕机时仍可切换到备机房数据库,保证业务的可用性。

单机房宕机情况下的降级方案

当发生单机房故障时(以主机房宕机为例):

  • 在网络层面,由全局负载均衡器统一控制将所有回源请求分拨到备机房。
  • 在负载层面,对主机房的请求进行补偿性调度,拨分到备机房。确保没有请求访问主机房。
  • 在应用层面,将分布式统一服务框架以及数据队列等系统间的调用接口调拨到备机房处理。
  • 在数据层面,通过切换数据库中间件将数据读写操作调度到备机房。

而对于备机房宕机的情况,只需要完成网络层和负载层的流量切换即可,毋需应用层和数据层的操作。

单机房宕机情况下降级的拟真演练

系统架构设计虽然侧重于对系统结构层面的把握,但是同样不可忽视技术性细节和具体的实践操作。

看似完美的设计方案在实际的生产操作中总是会面临各种考验,某些细节上的龃龉往往会酿成意想不到的后果。因此,再完美的设计方案也必须经过实战的检验。

由于单机房宕机的情况本身过于极端,而在生产环境模拟单机房宕机的风险过高,因此苏宁开发了一套专门用于模拟单机房宕机的故障注入系统。

该系统通过短时间内封闭机器特定的出入端口来实现对机器故障的模拟。主要涉及对应用,缓存,数据库层面的故障模拟。

故障系统本身具有高度可配置性,用户可以根据所需要模拟的场景配置不同的故障注入任务。

每个故障注入任务都有默认的自愈时间,当超过自愈时间后会自动恢复端口状态,防止模拟故障时间过长引发事故。

对单机房宕机场景的模拟可以大致分为应用层宕机,缓存单库宕机,缓存集群宕机,数据库单库宕机,数据库集群宕机,单系统完全宕机等六个场景,基本可以覆盖单机房宕机的所有情形。

在混沌系统的加持下,就可以对系统多活降级方案进行全覆盖测试,从而保证多活降级策略在生产场景下的可用性。

高并发场景下的链路优化

对于涉及高并发场景的系统而言,多活部署仅仅是一种应对极端情况的兜底策略,而单纯的横向扩容也难免会因为边际递减效应的收束而无法满足业务需求。

当结构层面的优化无法进一步提升系统效能的时候,就需要深入到业务流程中进行链路级别的优化。

以砍价团下单支付流程为例,本链路涉及到和多个中台系统的信息交互,链路较为冗长,在高并发场景下可能拖累系统效能。

对此改进思路如下:

  • 对于下单支付的前置校验操作,如下单资格,库存等,在并发量很高的情况下配置降级,在用户下单前取消资格校验操作。
  • 在生成订单链路中,涉及到和中台的交互,采取异步化处理的方式,中台处理完毕后将结果反馈给砍价团平台系统即可,进而减少用户的等待时间。

而相应的这一改造思路也可以移植到一些对时效性要求较低的场景中。

如果系统本身只在特定的时间点面临高并发场景,那么可以选择专门开辟一个高并发代码分支,在面临大促,节日活动时用高并发分支取代常规分支即可。

这样可以避免因为不定期的各种活动而频繁对代码进行改造,进而减轻了代码层面的业务风险。

核心链路拆分解耦

链路级别的优化除了代码层面的异步化改造外,也可以对核心链路进行分流,将不同的链路流量引流到不同的集群上,拆分系统压力。

比如对于展示与购物链路,其所面临的流量压力存在显著差别,展示链路 TPS 要远高于购物链路 TPS。

因此在架构设计上,可以分别将代码部署在两个集群上,通过不同的域名对展示链路和购物链路进行分流。

同时对于域名配置降级开关,如果支付链路集群故障,可以通过修改配置将支付链路的流量回流到展示链路进行处理,从而进一步提高系统的容灾能力。

结语

笔者以为,对于承载复杂业务的系统而言,其受木桶效应的影响尤甚,因此要尽量平衡系统的各个要素,不可偏废。

当系统足够复杂,那么决定系统整体效能的往往是结构性层面的因素,各个模块和单元之间良好的协调关系是系统稳健运作的保证。

而系统的设计也应该以满足自身业务为第一要义,尽可能精简一些不必要部分。

虽然理论上庞大的系统集群具有更强的容灾能力,但是过高的维护成本也会成为系统的拖累。

因此,明确自身业务需求,选择适合的方案,才是系统设计的要义。

本文转载自技术琐话公众号。

原文链接: https://mp.weixin.qq.com/s/25qQqA-oUrYL9U89vjjX0w

2020 年 4 月 23 日 17:37 445

评论

发布
暂无评论
发现更多内容

啃碎并发(二):Java线程的生命周期

猿灯塔

一致性 hash 算法

Z冰红茶

一致性Hash算法

联想来酷广谱化生存:后疫情时代的"硬核品牌"启示录

Geek_116789

week05 学习总结 分布式缓存&消息队列&负载

Z冰红茶

很多人毕业多年以后,还是改不掉学生思维

小智

职场 思维方式 高考

架构师训练营第5周

大丁💸💵💴💶🚀🐟

架构师训练营第五周作业

一剑

一次非常有意思的 SQL 优化经历: 从 30248.271s 到 0.001s

Java小咖秀

MySQL 面试 经验分享 优化逻辑 后端开发

啃碎并发(三):Java线程上下文切换

猿灯塔

Spring核心原理解析

Chank

Java spring

架构师训练营 - 第 5 课总结 -20200704- 技术选型

👑👑merlan

负载均衡 缓存 分布式数据库 架构设计 消息队列

Java 线程池中的线程复用是如何实现的?

武培轩

Java 程序员 后端 线程池 源码解析

【Python】__name__ 是什么?

Leetao

Python Python基础

「深度解析」AI训练之数据缓存

焱融科技

人工智能 AI 存储 焱融科技 数据缓存

女同事问哪吒什么是 Spring 循环依赖?我...

通天哪吒

第 5 周作业:一致性 Hash 算法

姜 某某

架构师训练营第五周课后总结

Cloud.

数据分析师成长体系漫谈--数据埋点

analysis-lion

数据分析 数据采集 埋点

第五周作业

Linuxer

极客大学架构师训练营

SpringBoot 中使用 Filter 的正确姿势

Java课代表

话题讨论|作为一名程序员,你下班之后都会做些什么?

InfoQ写作平台

写作平台 话题讨论 话题

用进废退,增加能力熟练度与经验值,让你的技能再次精进。

叶小鍵

干货 | 如何评估Kubernetes持久化存储方案

焱融科技

Kubernetes 容器 云原生 k8s 容器存储

计算机操作系统基础(十四)---线程同步之条件变量

书旅

php laravel 操作系统 进程 线程’

这份高考卷,只有程序员能得满分...

程序员生活志

程序员 高考

一口气说出 OAuth2.0 的四种授权方式

程序员内点事

Java oauth2.0

Ceph数据恢复初探

焱融科技

焱融科技 文件存储 分布式存储 数据恢复 Ceph

一致性hash的理解与实现

dongge

小师妹学JVM之:cache line对代码性能的影响

程序那些事

JVM 小师妹 性能调优 cache line

超详细!一文带你了解 LVS 负载均衡集群!

JackTian

Linux 负载均衡 运维 LVS 服务器集群

游戏夜读 | 关卡设计的难点

game1night

云原生来袭,企业上云如何平滑迁移增效避险?

云原生来袭,企业上云如何平滑迁移增效避险?

苏宁“砍价团”高可用、高并发架构实践-InfoQ