零点提交订单峰值破 1 万 TPS,单日总订单量超 500 万,活跃用户数 200 万,苏宁 88 拼购日活动取得了丰硕的成果。
在大流量、高销量的背后,是我们近半年来付出的努力,针对拼购系统瞬时高并发能力的优化与升级,才能保证消费者丝滑顺畅的购物体验。下面就来介绍下苏宁拼购系统应对大促的术与道。
术为用:拼购系统高可用的架构设计
“术”是架构设计的方法论。拼购系统的架构历经多次更新和迭代,其目的是打造一个高可用、高性能、易扩展、可伸缩性强的应用系统。
将整个过程精炼出来,我们主要做了三个方面的工作:
系统架构优化设计
数据库性能的优化
应用高可用的优化
系统架构优化设计
根据康威定律,组织形式等同系统设计。拼购系统之前为了满足快速的开发迭代节奏,所有功能是放在一个集群中的。
随着业务的发展,功能越来越复杂,单一集群已经成为限制系统性能的最大瓶颈。
图 1:苏宁拼购系统架构设计
因此,系统优化所做的第一件事情就是对拼购系统架构进行了重构。一方面,进行横向切分,将系统分层,包括:
网络层:通过 CDN 加速响应。一方面 CDN 缓存提高静态内容访问速度,减少服务端压力;另一方面,CDN 内部网络专线,加快回源速度。
负载层:包括四层负载和七层负载。功能包括流量调度、流量控制、安全防护、黄牛防护等,另外在负载层也做了一些轻量级业务的 Lua 聚合,以提高响应性能。
应用层:这层主要实现业务功能逻辑。
服务层:为应用层提供原子服务,如会员、领券、寻源、时效、生成订单、支付等。
数据层:提供数据存储访问服务,如数据库、缓存等;提供数据抽取分析服务,如 Hbase、Hive。
另一方面,针对拼购的业务特性,进行纵向切分,将原来耦合的功能逻辑拆分为三层:PGS-WEB、PGS-TASK-WEB、PGS-ADMIN_WEB。
每个模块独立集群部署,集群之间通过分布式远程调用与服务层提供的原子服务协同工作。其中:
PGS-WEB:前台业务处理模块。包括展示、交易、营销三个单元模块。每个模块又能分割为更小粒度的子模块。
比如营销模块就又能细分为四个轻量级玩法模块:邀新团、砍价团、膨胀红包和助力团,可以针对业务需要,对不同模块进行拔插、拆分和扩展。
PGS-TASK-WEB:中台定时任务处理模块,主要用于处理定时任务,另外支付逻辑也在这一层。
PGS-ADMIN_WEB:后台管理模块,主要用于运营人员维护活动、商品、玩法等。
数据库性能优化
在高并发场景下,提交订单、生成拼团记录、查询订单等操作,会对数据库造成很大压力,而这些一致性要求高的操作又不能直接使用分布式缓存替代数据库。
而给数据库降温,提高数据库的并发处理能力,必须让数据库具备横向扩展能力。因此我们基于 Mycat 数据库中间件实现了拼购系统数据库的分库分表策略。
图 2:高并发场景下 MySQL 数据库负载能力趋势
Mycat 是 MySQL 分库分表中间件,和 Web 服务器的 Nginx 类似,是应用与数据库之间的代理层。
由于 Mycat 是开源中间件,这里对技术实现不做阐述,主要讲下它在拼购系统下是如何应用的。
如下图所示,业务逻辑的数据操作通过 Mycat 分为三个库 DataNode 1~3,这个分库过程应用本身是无感知的。
图 3:苏宁拼购系统基于 Mycat 的分库架构
每个库一写两读,针对团和团详情的操作分片规则基于团 ID(GROUP_ID),针对订单的操作分片规则基于订单 ID(ORDER_ID)。
另外,还有一个单独的 BackupDB 用于大数据抽取和数据备份,通过 Canal 保证 BackupDB 中是全量数据。
当 Mycat 出现问题时,我们可以通过应用层的数据源切换,降级为单库,保证业务。
应用高可用优化
对于应用层面的优化,主要包括分布式缓存和异步化两方面。
利用 Redis 分布式锁解决并发场景一致性问题
比如为了防止订单被重复处理,我们使用 Jedis 的事务 Transaction + SETNX 命令来实现 Redis 分布式锁:
Transaction transaction = jedis.multi();//返回一个事务控制对象
transaction.setnx(tmpLockKey, "lock");//Set if Not Exist
transaction.expire(tmpLockKey, locktime);
List<Object> rets = transaction.exec();//事务执行
利用 Redis 实现活动库存,解决数据库资源竞争问题
对于每一个拼团活动,我们是维护了活动库存的,或者叫做资格/剩余数,并非真正的实际库存。
在 1 元秒杀等活动中,这个活动库存的变化会很迅速,大量数据库 UpDate 操作,造成行锁非常影响系统吞吐率。
优化方案是 在Redis 中做活动库存扣减,并以一定周期同步给数据库:
/**
* Redis缓存扣减活动库存
* */
private Long updateStoreByRedis(String actId, String field, int count) {
String key = redis.key(PGS_STORE_INFO, actId);
// 如果活动库存缓存信息存在,则更新对应field的数量
if (!redis.exists(key)) {
// 从数据库读取该活动库存信息并初始化到redis
ActivityStoreEntity entity = queryStoreInfoFromDb(actId);
if (entity == null) {
return -1L;
}
Map<String, String> values = new HashMap<String, String>();
values.put(PGS_STORE_ALL,…);
values.put(PGS_STORE_REMAIN, …);
values.put(PGS_STORE_LOCK, …)
redis.hmset(key, values);
// 若活动有效期内库存缓存信息失效,初始化缓存信息一小时
redis.expire(key, ONE_HOUR);
}
return redis.hincrby(key, field, count);
}
/**
* Redis同步活动库存给数据库
* */
public int syncActivityStoreToDB(String actId) {
…
try {
// 判断同步锁状态
String key = redis.key(PGS_STORE_SYNC, actId);
if(!redis.exists(key)){
更新活动可被锁库存数量
redis.setex(key, actId,STORE_SYNC_TIME);
}
}
} catch (Exception e) {
Log;
}
…
}
异步化操作,消除并发访问高峰
比如支付完成后,有一系列的后续处理流程,包括活动库存扣减、拼团状态变更等,其中有些逻辑实时性要求高,要同步处理。
有些则可以通过异步化的方式来处理,像通知物流发货。我们采用 Kafka 队列来进行异步通信,让下游系统进行消费处理。
道为体:拼购系统高并发下的保障体系
“以道驭术,术必成。离道之术,术必衰。”我们所有的架构优化与升级最终目的是为了保障促销高峰的稳定性。
拼购系统高并发场景下的保障之道,是以合理的容量规划为基础,全面覆盖的监控体系为支撑,形成的完善的限流+降级+防控策略。
全链路压测与容量规划
根据业务预估量,生产环境针对苏宁拼购全链路场景的压测,才能做出合理的容量规划。
目前,我们的压测系统,可以支持引流压测,即将线上真实流量复制下来,生成脚本,进行压测。最大程度保证了压测和真实情况的一致性,从而使容量规划更精确。
端到端覆盖的监控体系
目前苏宁拼购的监控体系能够做到端到端的覆盖,包括客户端->网络->服务端的监控。
其中,客户端监控依赖于覆盖 PC + WAP + App 的终端日志。网络监控主要是 CDN 日志和拨测数据。
服务端监控手段最为丰富,包括:
服务器系统状态监控:CPU、内存使用率、网卡流量、磁盘 IO 等。
Web 服务器监控:实时展现 Web 服务器的 Http 连接数、响应时间、Http 异常、状态码等指标。
应用服务器异常监控:实时汇总应用异常堆栈信息。
JVM 状态监控:实时展现JVM的内存、线程、GC 和 Class 的使用状况。
NoSQL 监控:Redis 每分钟命令数、大对象、连通性等的监控。
数据库监控:数据库层面各项指标监控。
调用链监控:实时展现应用间调用关系,反馈链路系统健康状况。
这些监控系统通过 traceId 相串联,与基础运维平台打通,最终通过决策分析平台聚合,实现智能告警。
图 4:端到端监控体系与告警决策平台
流量控制与风险控制
流量控制是针对 88 拼购日零点峰值疯狂流量超出预期,所设置的限流,以保护好自身应用,否则出现雪崩式连锁反应。
目前拼购的流控系统可以支持多个维度的流控策略。包括最基础的 JVM 活跃线程数流控,针对用户 IP、UA 和会员编号的限流,针对核心接口的限流策略,针对爆款商品的限流策略等等。
图 5:拼购流控系统架构
风险控制是针对 88 拼购日爆款商品被黄牛刷单风险的防控策略。除了传统的黄牛库名单,拼购的风控策略包括对用户、地址、事件行为、设备指纹等的判断。
区别于非黑即白的防控,拼购采用打分的方式对用户进行画像,对潜在的风险用户采取短信验证、滑动验证、人脸识别等一些列挑战模式。
大促准备与应急预案
大促准备工作是指结合业务的促销节奏,梳理的一系列大促准备工作,包括非核心定时任务的提前降级、生产操作权限的回收等。
应急预案是针对大促可能发生的突发性事件梳理的预案,应急预案是建立在降级手段的基础上的。
比如关键时候对部分功能的降级关闭操作,弃车保帅,保障购物流程的正常;再比如针对服务器性能瓶颈的降温手段,只有在准备好应对一切突发情况的前提下,才能保证每次大促的顺利完成。
结束语
路漫漫其修远兮,今年的 88 苏宁拼购日已经告一段落。未来向我们提出了更多的挑战与机遇。
如何进一步突破系统性能瓶颈,如何给用户提供个性化的推荐与服务,如何将拼购做成一个开放的社交化电商平台,苏宁拼购技术团队要做的工作仍然有很多。
我们将继续前行,势不可挡,并为大家带来持续的技术分享与更新。
作者:朱羿全、任章雄、张涛、龚召忠
简介:朱羿全,南京航空航天大学硕士研究生毕业,苏宁易购消费者研发中心高级技术经理,主要负责易购各系统架构优化与大促保障工作。先后参与了易购整站 Https 改造、苏宁拼购架构改造、先知业务监控平台建设等工作。专注于打造高可靠、高性能、高并发服务系统的技术研究。
编辑:陶家龙、孙淑娟
投稿:有投稿、寻求报道意向技术人请联络 editor@51cto.com
精彩文章推荐: