<audio id="audio" title="25 | 稳定性实践：开关和预案" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/35/f9/35d1a3ea3fa8b41b3a4ad430f40ed1f9.mp3"></audio>

在稳定性保障中，限流降级的技术方案，是针对服务接口层面的，也就是服务限流和服务降级。这里还有另外一个维度，就是业务维度，所以今天我们就从业务降级的维度来分享，也就是**开关和预案**。

## 如何理解开关和预案

**开关，这个概念更多是业务和功能层面的，主要是针对单个功能的启用和停止进行控制，或者将功能状态在不同版本之间进行切换。**

在业务层面，就像我们前面经常提到的大促场景案例，我们会关闭掉很多非核心功能，只保留交易链路的核心功能。比如我们认为商品评论是非核心功能，这时就会通过开关推送这种方案将这个功能关闭。当用户访问商品详情页时，这个接口就不再被调用，从用户角度来说，就是在大促峰值时刻看不到所浏览商品的评论列表。

在功能层面，我们技术架构中会使用缓存技术，但是要考虑到缓存有可能也会出现故障，比如不可访问，或者数据错乱异常等状况，这时我们就会考虑旁路掉缓存，直接将请求转到数据库这一层。

这里有两种做法：一种做法是通过我们上一篇介绍到的降级手段，也就是我们常说的熔断，自动化地旁路；另一种做法，比如在数据异常情况下，请求是正常的，但是数据是有问题的，这时就无法做到自动化旁路，就要通过主动推送开关的方式来实现。

**预案，可以理解为让应用或业务进入到某种特定状态的复杂方案执行，这个方案最终会通过开关、限流和降级策略这些细粒度的技术来实现，是这些具体技术方案的场景化表现。**

我们还是接着上面的这个案例来讨论。因为每个业务或应用都会有自己的开关配置，而且数量会有很多，如果在大促前一个个推送，效率就会跟不上，所以我们就会针对某个应用的具体场景，提供批量操作的手段，通过预案场景将同一应用，甚至多个应用的开关串联起来。

比如上面提到的商品详情页，我们不仅可以关闭商品评论，还可以关闭商品收藏提示、买家秀、店铺商品推荐、同类型商品推荐以及搭配推荐等等。有了场景化的预案，管理和维护起来就会更容易。

除了业务层面的预案，我们还可以将预案应用到应急场景下，比如上面提到的缓存故障异常。在真实场景下，要考虑得更全面，比如缓存能够支撑的业务访问量是要远远大于数据库的，这时我们就要做功能降级，这就要考虑数据库是否能够支撑住这么大的请求量（通常情况下肯定是支撑不住的）。所以，遇到这种场景，我们首要考虑的是限流，先将业务流量限掉三分之一甚至是一半，然后再将功能降级到数据库上。

这样就又涉及到多种策略的串行执行。如果没有预案都是单个执行的话，效率肯定会低，而且还可能涉及到多个应用都会执行相同的业务降级策略，这时就必须要有预案来统一管理，提前梳理好哪些应用需要在这种场景下执行对应的开关、限流和降级策略。

## 技术解决方案

技术方案上并不复杂，开关的字段主要以Key-Value方式管理，并从应用维度，通过应用名管理起来，这个对应关系就可以放到统一的控制台中管理。

下图是整个开关和预案管理，以及推送的示意图，我们一起分步骤看一下。

<img src="https://static001.geekbang.org/resource/image/b6/f6/b6f09f054d05cf429f5e3b40e73c1df6.jpg" alt="" />

1.**开关管理**

通过上述我们所说的Key-Value方式保存，与代码中的具体Field字段对应起来。这里就又会涉及到我们上篇内容中讲到的Spring的AOP和注解技术。

如下面代码所示，我们通过注解方式定义了一个开关testKey，它与控制台中配置的Key相对应，并获取对应的Value取值，在业务运行阶段，我们就可以根据这个值，来决定业务执行逻辑，下面是简化的示例。

```
@AppSwitcher(key=&quot;key1&quot;,valueDes = &quot;Boolean类型&quot;)

 private Boolean key1;

代码中直接调用AppName对应的开关配置，进行不同业务逻辑的实现：

Boolean key1 = MoguStableSwitch.isStableSwitchOn(&quot;key1&quot;);

if (key1)
{
//开关打开时业务逻辑实现
}else
{
//开关关闭时业务逻辑实现
}

```

2.**开关推送**

当在控制台上修改开关值后，会推送到微服务的配置中心做持久化，这样当应用下次重启时依然可以获取到变更后的值。还有另外一种方式，就是通过HTTP的方式推送，这种情况的应用场景是，当第一种情况失败时，为了让开关快速生效预留的第二个接口。

3.**配置变更**

应用中引入的开关SDK客户端会监听对应配置的变更，如果发生变化，就会马上重新获取，并在业务运行时生效。

4.**预案执行**

就是多个开关策略的串行执行，会重复上面这几个关键步骤。

关于开关和预案的内容，我们今天就介绍到这里。留一个问题，我们在上篇文章中介绍到限流降级方案的难点，请你思考一下，我们今天讲的开关预案这个内容，可能会遇到哪些难点呢？欢迎留言与我讨论。

如果今天的内容对你有帮助，也欢迎你分享给身边的朋友，我们下期见！


