Skip to content

代码里的那些兜底策略

在开发中,或多或少都会使用一些兜底策略,以应对一些突发的情况。常用的一些兜底策略有哪些呢?

开关

开关是比较常用的一个兜底策略。场景:

  • 有些接口的流量平时不怎么大,但是某一天猛增,系统是承受不住的,如果有开关,可以直接将开关打开,拒绝所有的流量,以保护系统。微服务架构的熔断,可以理解为更高级的开关,里面可以配置很多熔断策略
  • 流量开关。这个在已有项目中迭代新的功能且要求指定时间切换新代码会经常使用。使用流量开关,可以先将代码部署上去。然后在某个时间点开启开关,流量就会打到新的逻辑上。如果开关一直不开,则一直走的是老逻辑。当然,如果开启开关后,发现有问题,可以将开关关闭,就切换到老系统了。可以理解为响应迅速的回滚策略。大部分企业的回滚都不是迅速的,是有一点的耗时的,因为要重启上一个版本。如果企业想做到迅速的的回滚,则需要保留老版本一段时间,比如半小时左右,只是老版本不打流量上去而已,这里就类似开关关闭了。
  • 日志开关。这种在排查问题时会使用,写个开关,临时打印日志,如果问题排查结束后就把日志开关关闭以免影响正常的日志打印

开关的使用很灵活,在实际项目中自行决定如何使用哈,只要能达到你的目标就行

try-catch

try-catch一段代码,即使有异常,也catch掉,不影响主流程。这个场景相信大家用的很多,因为有些场景我们并不能控制,比如调用第三方接口,他们的接口并不是一直稳定的,所以需要try-catch一下做异常处理,不影响我们自身的主流程业务。

最近我在监控网关的超时日志中有使用。

@Slf4j
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
try {
// 省略....
log.error("网关超时,超时url {}",path);
} catch (Exception e) {
log.error("log gateway timeout error,nothing",e);
}
return super.getErrorAttributes(request,options); // 不更改框架原有逻辑,super
}
}

DB兜底

  • 缓存。结合缓存(堆外缓存、堆内缓存)的业务场景中会使用,例如Redis读取不到数据,就读DB
  • 本地消息表。这个在使用消息队列解耦时会使用。会使用本地消息表加一个定时任务做一个兜底。如果消息丢失了,DB中还有,然后由定时任务来进行触发执行业务
  • 搜索引擎。如果搜索引擎(ES)搜不到,去查DB。题外话,有没有感觉很相似,这个是不是类似MySQL的检索方式,先看看能不能用到索引,如果用不到索引,就全表扫。

主线程兜底

最常用的就是线程池的一个策略,CallerRunsPolicy策略,交给线程池调用所在的线程进行处理。

日志兜底

日志,可以理解为数据快照。在排查问题时,及其有用。有日志,可以事半功倍,没有日志只能一边看代码一边猜如果代码很长,很绕,很容易晕的。所以在业务代码主流程的关键位置打好日志是十分有必要的。

默认值兜底

举个例子大家就明白了。

Integer total = 0; // 总数默认为0,很少有人会写 Integer total = null;
Integer pageNo = 1; // 默认第一页等等

还有很多组件也会设置默认值,都会设置一些Default value,例如redission的nettyThreads的模式值就是32。也正是因为组件的默认值,使用组件的门槛才会降低,相对应的使用者会越来越多。足够简单,才会吸引人。

提示兜底

话术提示是软件工程上一个比较容易忽视的点,大多数软件工程师都会忽略。但是不可否认的是,一个良好的提示可以让一个非常严重的错误以一种温和、幽默、诙谐的文字来告诉用户,从而为恢复工作争取到更多的时间。对自己、团队、公司、用户都有利。

在软件工程上要维护好提示,提示要与时俱进。

如果你觉得提示兜底代码里的那些兜底策略这个主题不太相符,那我说一个非常专业的领域你也许大概就明白了。社会工程学攻击,好的攻击,攻击对象会无感,即使有些攻击很暴力。

Last updated: