大促备战方法论

参与人数多门槛低

1

 促销基本形式相同

不论是何种形式的促销:线上或者线下,都有着基本相同的形式。即通过一定的价格优惠策略,在一段时间内提升自己的营业额。

首先,在不改变其它属性的前提下,比如产品质量、配套周边服务等等,想大幅度提高自己的营业额,最简单快捷的方式便是通过降低价格来提高消费者的购买欲望。

其次,当客户对降价后的价格习以为常之后,购买欲望自然会下降,随之销量会下滑。促销的主要盈利模式是薄利多销。如果不设置促销周期的话,销量下滑后,最终商家会落的血本无归。

不管是美国的黑色星期五以及中国的11.11、6.18,都是在一定时间区间内展开的大促活动。

2

电商促销门槛更低、玩法多、能量大

虽然传统促销和电商促销的基本形式大致相同,但在具体实施上差异还是较大的。

参与门槛低、人数多

首先在时间段上,线下促销更多选择的是节假日,比如国庆黄金周、元旦等。选择这些的原因是因为线下店需要客户达到门店才能开展购物行为。但电商的特点无需受制于地点的限制,使用手机即可随时随地的进行购物。

相比于线下促销,电商促销的参与门槛大大降低。相同的促销优惠,参与的人数远超于传统模式。

优惠玩法更多更灵活

其次,线下都是基于实物进行展示,促销也是基于对实物位置等的聚合来进行展示。当需要变换促销时,常常需要伴随着实物的移动,灵活性较差。电商的促销都是基于虚拟信息进行组合,能够灵活的组装。同时,随着大数据以及智能推荐的技术发展,商家能够根据客户的喜好推荐定制化的促销,对客户的吸引力更大。此外,秒杀场景,线下是玩不了的,不然门都挤爆了。 

这种灵活性、吸引力也给电商促销带来了非常大的不确定性,商家可以随时组装出一个爆款促销,吸引大量用户实时参与。正因为这种灵活性,导致了无法准确的预估,给促销准备带来非常大的压力。

预期GMV更大

因为参与门槛点、促销优惠灵活多变吸引力大,整体上电商的预期GMV相比线下方式会更高。

流量是线上电商最终载体

1

所有用户操作都是系统流量

不管是何种形式的促销,发放优惠券或者设置超值的价格优惠,最终都会转换为系统流量。

用户领取优惠券会转换为领取优惠券的系统流量,抢购低价商品会转换为添加购物车和下单的流量。

2

差异点是流量变化

既然用户的所有操作最终都会转换为系统流量,那么在大促期间系统和平时的最大差异点就是系统流量的变化了

当系统流量出现变化时,我们就需要提前思考下列问题:

1. 预估会有多少流量的变化?

2. 流量变化的形式是什么?

3. 承载预估的流量我们需做哪些事情?

4. 流量超过预期或者承载范围之后,如何处理?

3

流量变化形式

虽然都是流量的变化,但是流量变化还是有很多种不同的形式的。对于大促的流量变化总体上有以下几种:

1. 首先,总调用次数会增加。最直观的就是用户使用次数增多,进而系统的整体调用次数也会增加。

2. 其次,单次操作的数据量会增加。因为一些促销的限制(比如满减促销或者定额的优惠券),用户单次操作的数据流量也会增加。比如:购物车的商品种类和总数量、下单的商品数量、命中的或者同时生效的促销种类。

3. 再者,会出现瞬间爆点。和传统线下促销相比,电商促销会出现瞬间(秒级)的爆发流量。因为线上促销不受传统的实体店大小、用户必须到现场、收银台结账等因素的限制,用户可以通过手机在瞬间对超值商品进行抢购。

从系统和管控双管齐下应对流量变化

首先,大促应对流量变化的准备,就是各个电商子系统对各自流量变化的方法面面的准备。所有大促期间增长、变化的流量最终都会落地到各个子系统上,只是各个系统根据所处的电商链路上的位置分配到的流量会有一定差异。比如商品、价格、用户等前置系统所分配的流量会偏多,因为它们是电商的前置环节。对于购物车、订单,则根据促销力度等因素会有一定的流量降低。

其次,即使在系统层面已经做好了充足准备,也要在非技术层面做好软准备。不要因为在某个火爆促销生效时,重启一下机器;在运营设置某个促销时,调整域名的解析;虽然影响的时间和平时是相同的,但在大促时产生的业务影响却更大。

另外,在安全领域同样有非常多的技术手段进行防护,比如防火墙、页面水印、大数据分析、人脸识别、数据加密等手段,但是被拖库、信息泄露等安全事件仍然屡见不鲜。这些事件的原因大部分都不是技术问题,大部分都是社工导致,系一些内鬼泄露、用户误操作等。因此,即使技术手段做到位,非技术方面也不能忽视,因为出问题的往往不是技术方面。大促备战也是一样。 

1

做准预估与做全应对在系统层面必不可少

3.1.1 流量预估是大促备战的头筹

如果应对流量变化是大促的最主要工作,那么准确的评估会变化的流量就是重中之重。

3.1.1.1 首先依据历史数据进行推测

当前我们已经处在数据时代,各行各业的决策都在从传统的依靠经验和第六感转换为通过数据进行科学、理性的决策。同样,我们的流量预估也需要依据数据更加准确的预估。大致有以下几种预估方式:

1. 基于历史瞬间峰值进行倍数评估

首先基于GMV进行计算。先统计出上一次大促区间实质产生的GMV,同时对此次促销区间将产生的GMV进行提前收集汇总。

基于上一次大促期间的各个系统的瞬间峰值,计算本次大促的峰值:

峰值增长的倍数就比较简单了,如下:

   

其次,在计算整体区间的GMV增长之外,还可以从某一个时间点生效的促销的角度计算增长。因为GMV是整体区间的预估,目前还不能准确评估在某个瞬间会产生多少GMV。但我们可以基于数据存储准确的评估到在某个促销点生效的促销数量,依据以下公式即可以计算出增长倍数:

  

2. 根据数据统计进行单次流量变化统计

除了对于整体流量增长的关注之外,也需要对单次调用的流量数据进行分析和评估。在同等的调用总量的情况下,一次获取或者输入数据量增大对系统的压力也会发生非常大的变化。比如:

1. 一次调用查询200个商品的价格以及一次下单200个商品相比一次一个的查询或者下单,性能势必会下降。这种下降有可能导致redis连接不释放,从而导致连接数暴涨进而带来雪崩。此外,如RPC框架(如JSF)等的线程池被占满也会带来雪崩。

2. 虽然调用量相同,但在大促期间查询的商品等数据更多的聚集在某几个商品上就会出现数据热点,会出现数据的热点导致分片被打挂的情况。

针对上述情况,我们需要根据各个模块(如商品、购物车、订单等)的接口数据(日志)进行分析,得出在大促期间单次请求的变化情况。

通过这种分析,我们就可以得出上一次大促中,全部流量中不同数量的占比: 

其他的区间数据依次类推,通过这种方式就可以得出本次促销的数据分布:  

同时,也可以得出数据热点的占比:

3.1.1.2 其次需要关注项目带来的变化

上一章节的计算是在两次大促期间没有做任何变动,只是预估GMV增长的情况下进行的。但在此期间的新需求和项目上线带来的影响是不可忽略的。

比如上次大促对于中台系统,只有三个业务方接入。但此次有新的业务方接入,最终的促销增长峰值需要在上述计算基础上进行再次的扩大。

对于这种新上线的系统,可以采用类似如下公式进行预估:

这种是总量的评估,可以结合上面提高的瞬时值和促销总量,做一些更加精准的评估。

3.1.1.3 最后不要遗漏使用方式&技改带来的变化

虽然上述两点已经从我们能够感知到的变化进行了计算预估,但我们也不能掉以轻心以为万事完备了。有很多我们无法主动感知的细小变化的点也会引起流量的巨大的变化。

1. 前台调用方在上次大促使用了前置缓存,此次大促因为延迟原因不再使用。

2. 前台调用方将以前使用的聚合调用,改为原子化的调用方式,带来流量成倍的增长。

3. 前台将我们的接口包装一次再对外以开放平台的方式进行暴露,可能会带来预期之外的流量。

4. 其它,...

这些变化都是我们无法主动感知到的但最终也都会带来非常大的流量变化。需要我们在备战的时候,主动的和我们的调用方进行线下提前沟通,提前预知变化,主动行动。

3.1.1.4 统筹各项变化预估最终变化

通过以上的数据计算和评估,可以综合得出本次大促预估的流量增长倍数、热点数据比例、单次请求里数据量的变化及占比。

通过历史数据、新项目、技改的叠加计算,可以得出:

最后,可以看出整个评估过程非常依赖数据。这就给我们提出一个要求,对于每一次的大促需要充分记录数据、记录真实的数据。

目前,咱们团队每次大促的所有数据和材料都进行了归档,以备后续使用。

3.1.2 系统备战紧贴流量变化

3.1.2.1 压测是应对流量变化的重要演练

现在,已经预估出大致增长的流量,我们就可以根据预估的流量进行压测。

只要压测的场景和历史大促场景无限趋同且所有压测中出现的问题都得以解决和修复。理论上,我们就可以平稳度过大促流量的挑战。

对于压测,首先需要确定边界。

即哪些系统、接口需要进行压测。大致有以下几类:

1. 新上线的接口。对于这种未经过大促验证的接口,毋庸置疑的需要进行压测

2. 从未压测过的接口也要进行补齐数据进行压测

3. 上次压测达到的最大峰值未达到本次大促预估峰值的接口。虽然可以通过增加机器来进行数学理论上的推算,判断基于上次压测结果:可抗峰值=单机峰值*扩容的机器数,来验证。但毕竟这是理想情况,很多事情扩容是不满足线性增长的。

其次,要进行贴近场景的压测。

如何贴近场景的压测呢?

就是使用大促期间用户真实的数据进行模拟压测,这里的模拟压测包含单模块或单接口的压测以及场景化混合压测。

对于单模块或单接口模拟压测需要注意几点:

1. 首先,压测数据不是随便伪造即可,这种压测的结果严重失真。

2. 其次,对于真实数据并不是采集用户平时的数据。因为用户平时的采购行为和大促期间是会发生变化的,比如采购量等。需要将每次大促区间的用户调用日志进行收集,并在下一次大促区间进行使用。这一点在咱们团队已经在落地实践中,但还有加强空间。

3. 最后,压测的结果峰值需要**达到或超过**预估出来的峰值。当我们压测两台或更多台机器后的,根据压测数据进行水平扩容机器也许是满足大促峰值要求的。但这里我们忽略了一个因素,有些时候,我们依赖的存储或者下游接口是不具备水平扩展性的。

对于混合和多场景压测很多时候会被忽略,但是非常重要的一环。

1. 首先,某个接口压测满足需求后并不代表整体满足需求。因为有可能两个接口部署在同一个进程里,在单独压测时候各自满足要求。当如果对这两个接口共同发压的时候,原有进程就承载原有压测2倍的流量,可能就满足不了需求了。

2. 其次,电商是一个整体协同系统。即使我们对某一个系统进行了混合压测,也并不一定保证完备。有可能两个系统A&B同时依赖另外一个系统C。当压测系统A满足要求时,并不会保证当系统A和B同时起量时,整体上不会出问题。所以,需要各个系统按预估量同时起量进行压测。

最后,基于压测结果指导行动。

当我们完成所有范围内的以及各种场景的压测后,我们就可以依据这些数据进行进一步的行动了。

1. 首先,要做的是数据记录

在上述各个章节里可以看出,大促备战的各项环节对数据是非常依赖的。因此,对于压测数据的记录是非常重要且必要的。在我们团队里,所有的压测数据都记录到一个内部的CMS系统里。它能够自动同步部门内的所有应用以及对应的接口,节约了非常多的录入成本。

2. 其次,依据数据进行扩容

当我们按上述流程进行了单机以及集群压测,其实是不需要扩容了。因为上述压测要求压测值满足预估的峰值。

但有些时候,因为时间、机器资源等原因,无法百分百的压测达到预期峰值的要求。此时,可以参考以下公式进行降级计算:

首先,此公式称之为降级公式的原因是不够准确,只是理论上预估。其中,`扩容损失比`是指从一台机器变为两台之后,tps变化不满足线性扩容的损失比。`接口占比`是指在某一个瞬间,某个压测接口占总量的比例。

其次,需要注意的是,公式里的`单机压测TPS`是指机器CPU在40%使用率的值,而不是单机极限值。因为,在实际情况,不可能出现当机器CPU利用率在90%而不扩容的。

例外,无压测的行动指南

想象总是美好的,现实是残酷的。有些时候可能连压测时间都没有或遗漏了,此时如何预估扩容呢。可以参考下述降级公式:

需要扩容的机器即可得出:所需扩容机器=需要总机器核数-当前机器核数。其中,`大促最大cpu利用率`指的是在流量峰值值,机器的`cpu利用率`所达到的最大值。`日常机器cpu利用率`指的是`日常调用tps`所对应的利用率。

这里需要注意的是,如果有多个接口部署在同一台机器上,可以将多个接口的`大促预估峰值tps`进行叠加计算。

3.1.2.2 基于流量的视角对入、自身、出进行把控

对于大促备战的处理需要从全局的视角进行梳理,而不是盲人摸象式的梳理。盲人摸象式会出现不同人得出不同结论,但所有人的结论不能说完全错误,但都不合理完善的情况。盲人摸象的梳理,会让所有人都认为自己感触到的局部就是整个大象的全部,其实不然。

对于大促备战是不能出现任何局部遗漏点的,如果某一个局部点疏忽了整体就可能出问题。因此,大促备战的梳理需要从全局视角进行梳理而不是盲人摸象。

大促备战首当是关注流量,基于流量的全局梳理便是重中之重。从基于流量的全局视角梳理,可以得出如下的流量路径:

大促备战方法论_第1张图片

从上述的路径可以得出对于任何类型的应用都可以分为三个部分:

1. 流量进入(接口调用、任务触发等)

2. 逻辑处理

3. 流量流出(依赖下游、调用存储、返回数据)

可以通过一句原则进行总结,即为:通过`防备上游、做好自己、怀疑下游`的方式对全局视角进行落地备战。

 防备上游的备战边界

依据当前团队内部入流量的类型及其差异,将上游的入流量分为接口入流量以及MQ入流量,对症下药分别出具对应的具体方案。

对于MQ考虑的点以及应对方案如下:

1. MQ消息出现洪峰(例如价格变动等)

大促备战方法论_第2张图片

对于接口需要考虑的点以及应对方案如下:

1. 接口流量超过预期阀值

大促备战方法论_第3张图片

2. 查询区间过大

3. 查询结果过大

4. 写入数据过大

做好自己的备战边界

做好自己是指对应用本身进行梳理,即使我们防护了外部的异常流量,但本身的代码有bug,仍然会导致我们在流量在正常范围内的稳步上升时出现异常异常。

1. 在线和离线混合部署

2. 日志通过JSON等形式打印过多

怀疑下游的备战边界

在当前大都是微服务化以及中台思路建设背景下,我们自身依赖的下游都不只是支持我们一个调用方,基本上都是支持多个调用方。某一方流量出现暴增等异常都会导致其它调用方受到影响。

因此,在大促期间我们对任何我们的依赖以及下游,都需要抱着怀疑的态度。做好充分的防备以及对应的应急策略。

具体的排查边界按存储类、接口类以及公共类进行应对方案和注意事项梳理。

存储类-ES分析

1. 写读比例是否过高

存储类-数据库分析

1. 慢sql排查

2. 代码里是否存在大事务、伪事务

存储类-缓存

1. 是否存在大key

2. 是否存在热key

接口类-JSF&HTTP等

1. 超时后是否配置重试&时间是否合理

2. 是否有降级方案及备用分组

公共类

1. 是否吞异常

上述从`防备上游,做好自己,怀疑下游`的阐述和梳理目前没有穷尽目前大促备战所做的所有事项。因为篇幅原因,这里不在阐述,后续单开一篇文章进行详细描述。对于具体的排查和分析方法,推荐阅读和学习在工业界非常成熟故障排查和分析方法:FMEA。

3.1.2.3 做好监控,问题早发现

我们已经从全局的角度对整个系统进行了review,对发现的所有问题都已进行修复,希望能够保障大促期间整个系统运行稳定。

虽然修复了所有已有问题,但还是存在一些因素和情况是我们无法控制和预知的(比如:网线被挖断等),这些情况是我们无法避免,必须去面对处理的。

首先快速处理是必不可少的,这样能够减少问题的影响时间。但能够第一时间发现问题,减少发现时长也是必不可少的,这就需要我们在监控层面做到面面俱到,没有遗留。

上述对应用的review采用了全局化进行梳理,此处对于监控同样采用全局的方式。同样基于入流量、应用自身以及下游进行监控处理,确保在问题出现时能够第一时间发现。

1. 首先,基于入流量进行监控

对于入口流量主要监控流量波动、性能、可用率等。对于入流量按应用分为接口和消息。主要内容项如下:

  • 接口和消息的性能监控及阀值报警

  • 接口和消息的可用率及阀值报警

  • 消息重试数量的监控

  • 消息消费的积压监控

2. 其次,对于自身的监控

自身关注的主要是应用本身(即为进程)以及应用所在的机器监控

应用维度的:

  • 对tomcat、worker按端口和进程编号等进行存活监控

  • web应用基于url进行存活监控

  • 监控JVM的GC信息和内存

  • 对依赖下游(数据库/JSF/ES/...)按切面等方式统一监控可用率

  • 对依赖下游(数据库/JSF/ES/...)按切面等方式统一监控性能

机器维度的:

  • 应用所属机器的磁盘、网络、CPU使用率、CPU load、内存的告警监控

3. 最后,对于下游各依赖的监控

总的来说,下游包含依赖的外部RPC服务方、各类存储(数据库、ES、缓存等等)。其中下游RPC服务是第三方团队专属运维,对于使用方是黑盒无需关注。而存储类对于使用方是白盒,使用团队可以主动去关注并监控。对于存储类的可以复用应用自身的监控点,主要如下:

  • 对于数据库机器的内存、CPU利用率、CPU load、磁盘进行监控

  • 对于ES的XXX监控

  • 对于缓存的XXX监控

上述从全局的方式按三个维度对大致需要的监控项进行了梳理,但只是从全局进行梳理和鸟瞰,没有穷尽所有项。其次,对于每一项的具体实施标准,并没有详述。此处因为篇幅原因,不再过多赘述,后续单开篇幅阐述。

3.2 管控演练易忽视,最后一环要重视

首先,我们在[第三章开头](#3. 从系统和管控层面双管齐下应对流量的变化)已经拿安全事件进行举例了,即使做了非常多的技术方面的准备,仍可能会出现安全问题,大多原因是集中在被忽视的非安全领域。对于备战是一样,在关注技术之外还需要关注非技术因素的事项。主要体现就是在促销期间,我们会做的一些动作,比如扩容、需求上线等。

其次,即使做了技术与非技术的准备。从历史经验来看,仍然会有漏网之鱼,各类事故时有发生,无法完全规避。既然出现问题是无法完全规避的,那么对于出现问题的修复就需要十分重视。问题修复主要包含两个环节:

  • 问题及早发现

  • 能够快速修复

及早发现已经在[监控章节]阐述了,现在就需要对快速修复的能力进行重点加强。下面先从如何快速修复问题谈起,再谈谈在非技术方面如何做。

3.2.1 按预案多练习减少试错时间

熟能生巧是一个非常简单易懂的道理,任何事情只要我们足够熟练,在这件事情上所花费的时间和成本也会非常的低。对于大促问题是同样的道理,只要我们对处理问题的技术、工具以及流程足够熟练,我们就可以缩短解决问题的时间。

加强熟练度的最简单的办法就是不断的练习同时保证是对真实场景演练。演练如果脱离了真实场景,整个效果就会事倍功半。因为真实场景的氛围、处理人面对压力下的反应等等因素,很多时候才是最重要的、决定性因素。

其次,演练的方案需要保证有效。如果花了大量时间,演练的不是能解决问题的方案,费时费力,那又有什么意义呢?

至此,快速修复问题主要就是应对两个方面:

  • 按真实场景的演练

  •  正确的处理方案

正确的处理方案(预案)要不断更新并日常化才能保证持续正确

现在基本上做过大促的团队都会有自己的应急预案。但随着时间的迁移,需求项目技改等都会不断的修改代码,就会带来一个问题,预案内容过时了,不再正确了。

当下一次大促来临,在短时间内梳理出所有的修改并完成预案的编写,基本上不太现实。时间紧、内容多、信息断层等等都会导致预案内容不完善。

针对这种情况,我们需要做到将预案编写和评审日常化、流程化。实现:

  • 做到代码改完就更新流程

  • 有新的处理方案就更新流程

  • ...

不断逼近实际故障场景进行演练

逼近实现场景主要考虑:

  • 首先,故障和实际一样

  • 其次,故障是突发而不是预先告知

和实际故障一样是为了保障故障真实有效,突发是为了保障处理人的心态和实际场景是一致的。

常见的捣乱猴子演练做到了故障真实有效,但很多时候是选择隔离的分组,同时在指定时间进行演练。效果并没有百分百体现出来,需要不断改进,能够做到使用正式分组在突发情况进行演练。如果对于此种情况能够快速处理,那么也有足够理由相信,在真正这个情况发生,也能够快速处理。

对于落地执行,考虑到对于业务的影响。可以小范围试行,再不断铺开推进。

毕竟罗马不是一天建成的,第一步或者前几步都是困难,因为路还有建成,路走通了,自然就顺畅了。

3.2.2 减少操作避免出错机会

不出错最简单的方法就是不做,对于大促备战的借鉴意义就是尽量不在此区间上线。

对于希望突破上线的,首先需要做的是对上线的内容进行定级定性进行分类,是否涉及到存储改造、是否影响到多条业务线等等。其次,结合需求的业务价值进行定义优先等级。

对于优先级较高确实需要上线的,就需要制定更高阶的、多层级的内容审查确保安全。

比如制定专项评审会,邀请架构师、测试以及产品多维度评估是否存在风险。对于代码,也可以参与多人review等机制,确保安全。

3.3 追求降本提效,给大促收益添砖加瓦

截止目前我们已经做了非常多的工作来保障大促稳定,如基于流量的预估、按预估流量的压测等等。虽然完成了这些备战工作可以更好的保障大促的稳定,但是不是足够了呢?

对于大促,从研发的角度来说保障稳定是首要目的。但追本溯源的去思考大促发起目的,应该是商家为了追求更高的营业额和利润而发起的。

追求更高的利润和收入有两种方式:开源和节流。开源是通过设置更有吸引力促销,提供更好的服务,获得更多的收入。节流是通过降低成本来提高利润,对于研发团队来说,降低成本就是降低备战所使用的时间和资源成本。

在备战事项不变的情况下,自动化是降低备战所使用时间和资源的首要手段。在整个大促期间我们已经做得以及可以做的自动化工作如下(未完全罗列):

大促备战方法论_第4张图片

 件件是精品  轻松过大促

我们已经从多个维度如业务预估GMV、技术以及非技术的流程管控上做了分析和讨论,针对大促备战出具各种措施和手段。是不是这样我们就可以高枕无忧,稳稳的度过大促的了?

结果往往不是,很多时候意外事件就意外的发生了。这些意外事件发生的原因可以用下面来进行形象的解释,一把箭穿过所有奶酪的孔。

大促备战方法论_第5张图片

上图是经典的奶酪模型,它由詹姆斯瑞森由1990提出来表示错误发生的原因。任何事项和活动都可以由类似奶酪的层组成,穿过奶酪的箭就是我们不想看到的意外事件。当奶酪的每个层都出现一个漏洞,那么箭就可以穿过所有层的奶酪,意外事件就这样发生了。

整个的大促备战和奶酪模型非常的类似,所有的大促备战事项就是奶酪的每一层。如果我们在落地备战事项时,每一项都是得过且过不力求完美,那么在所有事项都存在一两个漏洞时,大促事故这把利剑就会毫不留情的插在整个团队上。

相对贴近实际情况的案例大致如下:

1. 计算预估GMV时,只收集某几个大家认为的核心业务线的数据并基于此计算流量。

2. 压测时,随机伪造几个简单场景进行压测并以此判断应用的最大tps并扩容

3. 对于流量超出预期等等意外情况的应急预案不仔细评审并不断演练,只是靠经验处理

4. ...

可以看出,上述的每一件事情其实都是完成了但都存在一些漏洞。当某一个新业务线(比如今年推出的京喜平台)在大促时突然发力,通过各种营销手段提升销量。当这些洪峰流量到达我们系统时,上述备战方案多数抵挡不住。同时,对于这些意外情况的应急预案也是比较草率敷衍。最终,各环节的漏洞就会导致面对这些意外流量时捉襟见肘,导致系统上产生事故。

为了保障整个大促的稳定,我们需要每个备战项都做到足够好朝着100分迈进,对每个备战项怀着敬畏之心。

唯有如此,我们才有可能不被击穿奶酪之箭命中。

你可能感兴趣的:(大促备战方法论)