美文网首页Spring Boot 核心技术程序员二三事JAVA开发
500万日订单下的高可用拼购系统,到底暗藏了什么“独门秘籍”?

500万日订单下的高可用拼购系统,到底暗藏了什么“独门秘籍”?

作者: 恒宇少年 | 来源:发表于2018-08-29 22:58 被阅读83次

    零点提交订单峰值破 1 万 TPS,单日总订单量超 500 万,活跃用户数 200 万,苏宁 88 拼购日活动取得了丰硕的成果。

    image

    在大流量、高销量的背后,是我们近半年来付出的努力,针对拼购系统瞬时高并发能力的优化与升级,才能保证消费者丝滑顺畅的购物体验。下面就来介绍下苏宁拼购系统应对大促的术与道。

    术为用:拼购系统高可用的架构设计

    “术”是架构设计的方法论。拼购系统的架构历经多次更新和迭代,其目的是打造一个高可用、高性能、易扩展、可伸缩性强的应用系统。

    将整个过程精炼出来,我们主要做了三个方面的工作:

    • 系统架构优化设计

    • 数据库性能的优化

    • 应用高可用的优化

    系统架构优化设计

    根据康威定律,组织形式等同系统设计。拼购系统之前为了满足快速的开发迭代节奏,所有功能是放在一个集群中的。

    随着业务的发展,功能越来越复杂,单一集群已经成为限制系统性能的最大瓶颈。

    image

    图 1:苏宁拼购系统架构设计

    因此,系统优化所做的第一件事情就是对拼购系统架构进行了重构。一方面,进行横向切分,将系统分层,包括:

    • 网络层:通过 CDN 加速响应。一方面 CDN 缓存提高静态内容访问速度,减少服务端压力;另一方面,CDN 内部网络专线,加快回源速度。

    • 负载层:包括四层负载和七层负载。功能包括流量调度、流量控制、安全防护、黄牛防护等,另外在负载层也做了一些轻量级业务的 Lua 聚合,以提高响应性能。

    • 应用层:这层主要实现业务功能逻辑。

    • 服务层:为应用层提供原子服务,如会员、领券、寻源、时效、生成订单、支付等。

    • 数据层:提供数据存储访问服务,如数据库、缓存等;提供数据抽取分析服务,如 Hbase、Hive。

    另一方面,针对拼购的业务特性,进行纵向切分,将原来耦合的功能逻辑拆分为三层:PGS-WEB、PGS-TASK-WEB、PGS-ADMIN_WEB。

    每个模块独立集群部署,集群之间通过分布式远程调用与服务层提供的原子服务协同工作。其中:

    • PGS-WEB:前台业务处理模块。包括展示、交易、营销三个单元模块。每个模块又能分割为更小粒度的子模块。

      比如营销模块就又能细分为四个轻量级玩法模块:邀新团、砍价团、膨胀红包和助力团,可以针对业务需要,对不同模块进行拔插、拆分和扩展。

    • PGS-TASK-WEB:中台定时任务处理模块,主要用于处理定时任务,另外支付逻辑也在这一层。

    • PGS-ADMIN_WEB:后台管理模块,主要用于运营人员维护活动、商品、玩法等。

    数据库性能优化

    在高并发场景下,提交订单、生成拼团记录、查询订单等操作,会对数据库造成很大压力,而这些一致性要求高的操作又不能直接使用分布式缓存替代数据库。

    而给数据库降温,提高数据库的并发处理能力,必须让数据库具备横向扩展能力。因此我们基于 Mycat 数据库中间件实现了拼购系统数据库的分库分表策略。

    image image

    图 2:高并发场景下 MySQL 数据库负载能力趋势

    Mycat 是 MySQL 分库分表中间件,和 Web 服务器的 Nginx 类似,是应用与数据库之间的代理层。

    由于 Mycat 是开源中间件,这里对技术实现不做阐述,主要讲下它在拼购系统下是如何应用的。

    如下图所示,业务逻辑的数据操作通过 Mycat 分为三个库 DataNode 1~3,这个分库过程应用本身是无感知的。

    image

    图 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 Existtransaction.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 相串联,与基础运维平台打通,最终通过决策分析平台聚合,实现智能告警。

    image

    图 4:端到端监控体系与告警决策平台

    流量控制与风险控制

    流量控制是针对 88 拼购日零点峰值疯狂流量超出预期,所设置的限流,以保护好自身应用,否则出现雪崩式连锁反应。

    目前拼购的流控系统可以支持多个维度的流控策略。包括最基础的 JVM 活跃线程数流控,针对用户 IP、UA 和会员编号的限流,针对核心接口的限流策略,针对爆款商品的限流策略等等。

    image

    图 5:拼购流控系统架构

    风险控制是针对 88 拼购日爆款商品被黄牛刷单风险的防控策略。除了传统的黄牛库名单,拼购的风控策略包括对用户、地址、事件行为、设备指纹等的判断。

    区别于非黑即白的防控,拼购采用打分的方式对用户进行画像,对潜在的风险用户采取短信验证、滑动验证、人脸识别等一些列挑战模式。

    大促准备与应急预案

    大促准备工作是指结合业务的促销节奏,梳理的一系列大促准备工作,包括非核心定时任务的提前降级、生产操作权限的回收等。

    应急预案是针对大促可能发生的突发性事件梳理的预案,应急预案是建立在降级手段的基础上的。

    比如关键时候对部分功能的降级关闭操作,弃车保帅,保障购物流程的正常;再比如针对服务器性能瓶颈的降温手段,只有在准备好应对一切突发情况的前提下,才能保证每次大促的顺利完成。

    结束语

    路漫漫其修远兮,今年的 88 苏宁拼购日已经告一段落。未来向我们提出了更多的挑战与机遇。

    如何进一步突破系统性能瓶颈,如何给用户提供个性化的推荐与服务,如何将拼购做成一个开放的社交化电商平台,苏宁拼购技术团队要做的工作仍然有很多。

    我们将继续前行,势不可挡,并为大家带来持续的技术分享与更新。

    作者:朱羿全、任章雄、张涛、龚召忠

    简介:朱羿全,南京航空航天大学硕士研究生毕业,苏宁易购消费者研发中心高级技术经理,主要负责易购各系统架构优化与大促保障工作。先后参与了易购整站 Https 改造、苏宁拼购架构改造、先知业务监控平台建设等工作。专注于打造高可靠、高性能、高并发服务系统的技术研究。

    开源信息

    开源:Code-Builder代码生成工具

    开源:MyBatis-Enhance持久化框架

    开源:支持12种数据库的自动化分页插件

    安全系列

    使用JWT设计SpringBoot项目api接口安全服务

    使用SpringSecurity让SpringBoot1.x项目更安全

    QueryDSL系列文章

    SpringBoot与QueryDSL初整合

    QueryDSL与SpringDataJPA实现单表普通条件查询

    使用QueryDSL与SpringDataJPA完成Update&Delete

    QueryDSL与SpringDataJPA实现多表关联查询

    使用QueryDSL与SpringDataJPA实现查询返回自定义对象

    使用QueryDSL与SpringDataJPA实现子查询

    推荐阅读

    如何在SpringBoot1.x项目中使用拦截器

    SpringBoot添加支持CORS跨域访问

    SpringBoot1.x项目多模块运用与设计

    SpringBoot项目多模块打包与部署

    SpringBoot1.x项目整合JPA多数据源配置

    在SpringBoot内如何使用ApplicationEvent&Listener完成业务解耦?

    SpringBoot自定义专属业务的Starter

    SpringBoot2.0新特性 - 你get到WebMvcConfigurer两种配置方式了吗?

    SpringBoot2.0新特性 - 岂止至今最简单redis缓存集成

    SpringBoot2.0新特性 - Quartz自动化配置集成

    编码规范 - 养成良好的Java编码习惯

    基于SpringBoot 设计业务逻辑异常统一处理

    基于SpringBoot & Quartz分布式单节点持久化

    关注公众号

    image

    欢迎微信扫码加入知识星球,恒宇少年带你走以后的技术道路!!!

    image

    知识星球 - 恒宇少年

    相关文章

      网友评论

      本文标题:500万日订单下的高可用拼购系统,到底暗藏了什么“独门秘籍”?

      本文链接:https://www.haomeiwen.com/subject/ppslwftx.html