618几号开始预热 淘宝618什么时候开始预热

35白皮书 2023-01-31

每年 618 的大促都是一场技术团队大练兵的时候。作为技术研发人员在这场战斗中加深了对线上系统的敬畏之心通过系统的备战在技术上也得到了提升。大战在即如何保障系统稳定我们的备战思路是什么?

首先确定自己的备战思路梳理核心流程、找出薄弱点对薄弱点进行优化针对业务进行资源隔离同时协调压测对优化结果进行验证并针对场景准备预案比如降级开关在什么场景下开启以及降级之后的影响有哪些等等后面还要实际演练防止实际情况发生时准备好的预案不可用。

我们知道618 期间发生的问题都不是小问题所以如何做到更周密的筹备我们可以秉承如下几个原则:

墨菲定律:任何事都没有表面看起来那么简单;所有的事都会比你预计的时间长;会出错的事总会出错;如果你担心某种情况发生那么它就更有可能发生。 二八原则:19世纪末意大利经济学家巴莱多认为在任何一组东西中最重要的只占其中一小部分约20%其余80%尽管是多数却是次要的这就是二八法则。二八法则告诉我们不要平均地分析、处理和看待问题要把主要精力花在解决主要问题、抓主要项目上。

我们可以利用墨菲定律来梳理系统薄弱点那些总是小问题不断的地方一定会在某天酿成大的灾难必须尽早解决。还可以利用二八原则梳理黄金流程那些出了问题可能极大影响公司或影响用户的地方就是不容有失的黄金流程。

我们以此为思路从四个方面进行逐一介绍。

一、梳理薄弱点

1. 梳理系统架构

备战第一步就是要对系统做一次全面的梳理诊断其目的就是要甄别出系统的薄弱点。而梳理的第一步就是要梳理出系统架构。通过梳理系统架构梳理系统的层次结构和调用关系由此排查系统调用的瓶颈和薄弱点。如是否有服务节点是热点或单点服务是否有过长的调用链路是否存在业务耦合相互影响等等。

以我现在负责的京东服务市场为例服务市场采用 SOA 微服务化的架构设计理念将服务市场设计为模块化和层次化的架构风格高层次模块调用低层次模块低层次模块通过接口向上提供服务。而复杂的调用链路必然造成业务之间的相互影响所以通过对服务市场从部署、数据库访问、服务 PRC 调用、消息接收等进行了纵向垂直部署隔离实现即使任一垂直域无论因为服务器还是数据库问题影响不会扩散到其他业务上。这我会在后面详细讲述如何实现纵向垂直部署隔离。

2. 梳理系统薄弱点

无论系统架构是以哪种形式展示我们都希望能够通过梳理把系统的薄弱点甄别出来。那系统薄弱点都有哪些特征呢?通过哪些手段能检查出系统薄弱点?

我们想一下系统薄弱点可能引发哪些灾难严重的有系统宕机服务不可用性能下降404CPU OOM 等等所以可能产生上述隐患的点就是我们要关注的系统薄弱点。对于系统薄弱点我们可以通过以下几点进行排查:

检查没有脱库的功能:我们知道大促时的流量可能是平时的2~10倍所以一些平时性能还好的查询接口极有可能因调用量猛增造成性能的极剧下降如果其中的某些接口还是查询数据库的那可能就会给数据库产生极大的压力极端情况可能造成数据库 CPU 出现服务不可用因为数据库本身的架构设计就不是抗量的所以对没有必要查库的功能进行脱库改造以保证数据库的稳定是非常重要的尤其是那些严重依赖数据库的系统。 检查慢 sql:不是所有的功能都能实现脱库的所以慢 sql 的检查就是为了保证数据库的稳定因为一个慢 sql 就有可能把数据库的 CPU 打到一是由于慢 sql 最容易出现的是没有索引二是由于慢 sql 大多是关联查询、嵌套查询以及使用聚合函数等造成的还有就是慢 sql 大多需要回源查询大量的请求会造成查询能力特别慢还会造成其他请求无法获取连接影响正常服务。所以对于那些特别依赖数据库的系统要每日订阅慢 sql进行优化。优化就是优化数据库索引将慢 sql 变成快 sql。 检查 ump 和 log:人常说研发人员有两只眼睛一只是监控报警另一只就是日志所以无论什么情况监控报警日志一定不能少。因为有了监控就能做到即时发现问题有了日志就能做到迅速定位问题。否则出了问题就是两眼一抹黑一通胡猜。我经常看到当有些问题从客服那里投诉过来才发现要么该有的监控没有要么有监控却没有报警以及在解决问题时总是找不到关键的日志只能干着急。 检查 jsf、mysql、jmq 等 timeout:设置 timeout 实际上是一种快速失败策略使系统具备自我保护的能力。检查超时一是检查该有的设置有没有二是检查设置的时间是否过长。没有超时设置则系统就没有自我保护的能力这自不必说当大量请求连接因为长时间运行而无法得到释放时系统的资源很快就会被耗尽从而造成了服务的不可用。而过长的设置同样在访问量大的时候就会导致请求连接积压这也会导致系统的 CPU 快速飙升。那超时应该设置多少合适这需要具体情况具体分析但一个查询请求设置 10s 超时肯定是跟没设置一样的。 检查 redis 热 key、大 key、慢日志:热 key 和 大 key 是不同的热 key 是某些 key 可能会被突然暴增的流量大量访问这些 key 又碰巧集中存储在同一分片上从而使得该分片的 IO、CPU 等资源吃紧极端情况下迫使 redis 进行 failover 主从切换但切换后的瞬时流量可能又一下子击穿 redis迫使 redis 再次 failover如此反复服务几乎就是不可用的。而大 key 则是某些 key 存储的 value 很大或者结果集很多也是在可能暴增的流量下大 key 不合理的查询使得 value 在 IO 传输中阻塞这情况和慢 sql 很像又因为 redis 是单线程所以大 key 查询造成 IO 问题就会很容易凸显出来。所以如果检查出热 key 或大 key就建议进行修改。 检查 master-slave 同步延迟:master-slave 同步延迟很大可能造成数据不一致性这有可能影响业务正常运营如创建订单写到 master然后从 slaver 查询订单但由于延迟太大就可能影响用户查不到你可能会说这还好吧慢1~2s不会有太大影响但这个逻辑如果放在创建订单之后马上查询订单根据查到的订单创建结算在这个场景下如何查询 slave 找不订单就是不可接受的这可能导致流程代码出错。所以需要评估这种延迟是否对业务产生影响。 检查定时 worker 全量扫描任务:定时 worker 大多数是在运行数据订正的任务而数据订正往往又需要查询大量数据进行比对其实这都还好不好的就是几次深分页的查询可能造成数据库性能的极剧下降。所以检查定时 worker尤其要避免慢 sql 和大数据集的查询否则会造成人为的流量洪峰。还有检查定时 worker的执行频率检查不同 worker 的执行时间尽量避免不同 worker 同时执行检查定时 worker 执行的服务器是否单独部署。 与运营沟通 618 运营节奏应战调用量高峰:提前与运营沟通运营节奏可以做到知己知彼并可以预知流量可能流向哪里这可以有针对性的进行备战。

3. 梳理黄金流程

黄金流程也称核心流程什么是黄金流程就是那些出了问题可能极大影响公司或影响用户的地方就是不容有失的黄金流程。以服务市场为例服务市场包括服务发布、服务审核、服务订购、服务结算、服务使用共五个环节那其中哪些是重中之重的核心环节呢?服务市场是面向供需双方的双边市场以需求方为大头所以服务订购和服务使用是服务市场的核心环节。

梳理黄金流程我们要整理出它的大致流向和关键节点通过系统流向和关键节点发现系统设计不合理的地方以服务市场交易流程问题我们看到在履约中心订单完成后通过观察者模式发送消息、创建订购、推送结算的设计是存在风险的因为每个观察者失败了就失败了且每个观察者的业务逻辑都不是幂等逻辑。这种设计的不合理就必须通过梳理黄金流程来发现。

二、资源隔离

梳理依赖资源(docker、vip、mysql、jimdb、es、jsf、jmq)及目前资源的服务器状况是否健康(cpu、memory、dish 等)进行纵向垂直部署隔离实现即使任一垂直域无论因为服务器还是数据库问题影响不会扩散到其他业务上而这也是所谓的分离技术。

docker 资源

梳理系统 docker 资源部署情况设定 x 轴是各个业务线y 轴是业务系统整理出一张二维表根据之前梳理的系统架构图可以清晰的看到各业务线下各个业务系统在不同机房的部署情况如是否双机房部署是否有热备机房是否部署资源不合理等。机房1和机房2之间是两个不同的地域部署所以通过地域可以实现容灾。还有虽然系统都是一套代码但可以根据各业务线的具体情况某些业务线不需要的某些系统就不部署因为代码走不到那里并可根据业务调用量合理配置资源情况。

梳理资源一是梳理自己的资源部署情况二是需要对上下游依赖系统做到资源对齐(为了效率考虑目前很多应用正常情况是同机房调用)譬如:如果上游部署在3个机房机房A、机房B、机房C流量配比是5.3.2那我们也应该按照该比例部署。

* 数据仅为示例不是真实数据;表格形式仅供参考

vip 资源

检查 vip 配置避免因为 docker 扩容或缩容导致的 vip 配置错误将扩容的 docker 加到 vip 中将缩容的 docker 从 vip 中去掉。检查运营商的 IP 解析情况譬如需要支持电信、联通、移动、教育网等。当然并不是所有业务系统都提供对外服务。热备机房不挂 vip否则就会有线上流量了。当主力机房出问题的时候通过将问题机房的 vip 飘到热备机房从而实现流量切换。

* 数据仅为示例不是真实数据;表格形式仅供参考

MySQL 隔离

在 docker 资源的部署背景下我们绘制 mysql 资源的部署情况其中有些业务线下的机房虽然有部署系统但该业务线的流程是已经脱库的所以就可以不配置数据库。还有某些流程在某些业务线下只有读操作没有写操作的所以数据库只配置读库而不配置写库。以及最重要的不同业务线的应该使用不同的从库并根据查询数据库的业务量合理配置从库资源规格。mysql 与 docker 尽可能在同机房部署这也可以有效的实现系统容灾。

* 数据仅为示例不是真实数据;表格形式仅供参考

Redis / Solr / ES 隔离

绘制 redis、solr、es 部署情况与绘制 mysql 思路一样。特别说下服务市场部署 redis 与 mysql 不同之处在于redis 是一主一从架构各业务线共享同一 redis 集群但不同系统之间使用不同的 redis 集群所以如果 redis 挂了还是会影响所有业务线的而避免 redis 宕机则就要注意前面提到的检查 redis 的大 key 和热 key。

* 数据仅为示例不是真实数据;表格形式仅供参考

JSF 业务隔离

梳理业务依赖关系调整 jsf 调用链路通过 jsf 分组进行业务隔离这其实是最重要的也是最难的也只有把这个做好才能实现各业务线出了问题不会相互影响。我们知道一个系统里既有 provider 也有 consumer我们要做的就是保证在某一个业务线内该业务线内 consumer 一定调用该业务线内的 provider 的如服务市场的业务线 B那一定是大前台在业务线 B 下只能调用业务线 B 的商品中心、交易中心、服务引擎等而这实现就是通过 jsf 的分组别名这没有啥技术含量就是要了解业务依赖关系认真仔细。由于系统是一套代码对于那些有部署系统但不提供 jsf 的服务分组我们可以设置为 disable以此区分和隔离。

在进行 jsf 分组隔离时因为工作量很大很是耗神还有就会想到一个问题一个系统提供几十个甚至上百个 jsf 接口出去为什么分组命名会千奇百怪几乎是每个 jsf 分组都有自己的命名但仔细想来 jsf 接口可以做到细粒度隔离因为 jsf 可以实现进程隔离和线程隔离这使得即使是同一系统的 provider也可以实现进一步的细分业务来隔离各业务间的相互影响。

* 数据仅为示例不是真实数据;表格形式仅供参考

JMQ 业务隔离

jmq 大量应用在系统解耦的场景中而 jmq 同样有 provider 和 consumer它不像 jsf 那样灵活可以支持多个分组jmq 只能控制哪些系统生产消息哪些系统可消费消息。因为 jmq 是通过 topic 进行消息传递的我们不能给每个业务线申请一个 topic所以 jmq 的隔离更多的是梳理业务依赖关系对那些不会生产或消费 jmq 的业务线设置 topic 为 disable确定消息会被哪些系统生产又会被哪些系统消费。

* 数据仅为示例不是真实数据;表格形式仅供参考

三、压测

压测不是为了把系统压挂是否需要测出峰值需按照业务场景决定。很多业务可能就需要知道自己的峰值。压测一定是根据当前调用量进行评估以2~10倍为预期值进行压测压测还要选在掉用量少的时间并逐步加量。压测尽量做到服务器及依赖资源的隔离如果无法做到需要控制好量。尤其当压测会查询数据库的服务一定不要把数据库压出问题来如果压出问题就违背了压测的初衷压测是为了把系统的瓶颈压出来而不是把系统压出问题来。即使是完全读取缓存的服务也会因压测造成系统性能的下降因为数据在传输过程中的序列化和反序列化以及对多线程的切换都会造成 CPU 的飙升还有注意磁盘空间是否会被日志打满诸如以上等等问题不要因为压测造成一场线上事故。

压测我们要关注哪些是结果指标有并发数、TPS、TP99、成功率这些结果指标能有效反应服务的好坏以及关注被压测服务的 docker 的 CPU、内存、load 等系统性能。所以压测策略一定是逐步加量从并发10 - 50 - 100 - 200 - 500观察服务的 TPS、TP99、成功率是否有降低还有被压测机从 1 - 5 容器数增加对比单机服务性能是否有所提升。所以压测的目的是为了能检验服务能力是否支持可水平扩展即加机器就可以抵抗洪峰。

最后压测一定要制定计划今天压什么明天压什么还有跟兄弟团队打好招呼因为别的团队也会压测如果他的服务会调用到你这里而这是你也在压测这个接口结果超出预期的流量就可能造成意想不到的麻烦。

四、预案

预案准备至关重要它能保证系统在出问题时进行及时止损避免大出血。止损主要以降级开关来实现有损降级以保证核心黄金流程不受到影响。因为系统业务演进多是混沌的所以需要对系统的降级开关进行有效的梳理哪些是有用的那些是无效的还要协调兄弟团队一起沟通 618 备战方案确定演练方案联合备战。

对降级开关的梳理不能只停留在知道有这个开关的基础上还要知道这个降级开关在什么场景下开启以及降级之后的影响有哪些等等降级逻辑必须场景化否则出了问题开关开还是不开都没有一个准则。

总结

当然在备战 618 的过程中有很多事要做不仅仅是业务隔离和压测还要在梳理系统薄弱点过程中发现潜藏的问题进行针对性的优化改造。这里有个题外话要说备战过程中我偶尔会听到一些声音让我非常生气。

一是说”之前没出问题所以觉得就没问题“。我见过多少次线上事故就是因为思想上的怠慢问题并不是不知道就是本着侥幸的思想觉得之前没问题觉得现在就没有问题。这种想法我认为就是错的之前没问题不代表它不是问题是问题就是问题。我犯过错一个错误就是大上周上线报警出一个问题排查有个慢 sql 查库然后进行了修改不急不慢改一周准备第二天晚上上线结果第二天早上就被人刷了数据库 CPU 直接干到 80%好在这个功能有降级且业务之间进行隔离部署没有造成很大的影响。所以发现问题一定要即时修改。

二是说”之前的逻辑不是我写的我就改了这些那些代码我不太清楚“。这种说辞更不可接受你连你改的代码上下文都不了解你改什么代码你能不改错么。所以我要求大家之前的代码不管谁写的我不管了但现在代码在你手上你就必须负责把它写好不能总是个临时方案开个分支出去最后代码一团浆糊。举个例子上线新代码为了能实现快速降级通常使用开关进行切换这时系统里就会有新老两套代码那老代码什么时候删除如果当时写代码的人不负起责任代码就那么放着也不删那后面不仅系统代码会越来越臃肿而且代码的可读性也会越来越差。甚至不小心弄错了开关还会出现意外的彩蛋什么的。