如何设计兜底方案(高可用)
场景:
很多时候,在同步数据时,都会有一个重新推送的按钮,不管是重新推送还是重新拉去。这些动作都是失败后,再次操作,直到成功。
这种设计的原因是,程序的运行,不知道什么原因会失败。网络、数据库、服务器,B服务BUG都会导致这段代码执行失败,从而无法保证该功能准确执行。
在设计的设计中都是需要兜底的方式,或者冗余的设计
- 系统自动重试
- 主动备份,通过备份能够继续访问
- 人为操作程序来完成错误后的流程
框架程序中兜底的设计
1.OpenFeign框架的重试
OpenFeign请求框,虽然默认情况下没有开启重试的机制,但是只需要配置,请求调用在失败后,会进行重试。
@Configuration // 存储 Ioc
public class RetryerConfig {
@Bean
public Retryer retryer() {
return new Retryer.Default(
1000, // 重试间隔时间
1000, // 最大重试间隔时间
3 // 最大重试次数
);
}
}
2.Rocketmq中NameServer
rocketmq的nameServer挂了,本地已保存的配置在一段时间内能够保证服务能够送达。
试想我们有一个配置中心服务,它会定期更新线上各服务的配置参数。那么当我们设计服务时,其实是需要考虑到一旦配置中心出现故障,对我们服务的影响。
我们可以这么设计:首先提供一个本地配置文件(当然它的参数肯定比较过时了),作为任何异常情况下的兜底。其次将每次从配置中心上拉下来的配置参数,写一份到本地,这样即使后续读取失败,我们也会有一份比较新的配置参数可用。最后我们再开一个线程,实时拉取新的参数,进行更新操作。
按照这种方式,将服务从强依赖配置中心,改为了弱依赖。不过需要考虑到这种设计带来的复杂度,如拉取新的配置文件到写入本地磁盘,这一过程是原子操作的。
3.XXL-JOB的人工介入的重试
新开发的运营系统,需要每天凌晨跑一个job,这个job比较复杂,其中涉及到了多个端的调用
设计的方案:首先让它每隔一小时就启动判断一次,如果该任务当天已经完成了,就不再重新启动,否则开始运行当天的job;如果第一次job执行失败了,之后的job也会执行成功。
其次会假设万一当天执行的job失败了,我在配置文件中仍然会配置一个默认参数,这时直接使用该参数进行兜底。
高可用的实践方案
网络上高可用实践方案总结:
- 1、对等节点的故障转移,Nginx和服务治理框架均支持一个节点失败后访问另一个节点。
- 2、非对等节点的故障转移,通过心跳检测并实施主备切换(比如redis的哨兵模式或者集群模式、MySQL的主从切换等)。
- 3、接口层面的超时设置、重试策略和幂等设计。
- 4、降级处理:保证核心服务,牺牲非核心服务,必要时进行熔断;或者核心链路出问题时,有备选链路。
- 5、限流处理:对超过系统处理能力的请求直接拒绝或者返回错误码。
- 6、MQ场景的消息可靠性保证,包括producer端的重试机制、broker侧的持久化、consumer端的ack机制等。
- 7、灰度发布,能支持按机器维度进行小流量部署,观察系统日志和业务指标,等运行平稳后再推全量。
- 8、监控报警:全方位的监控体系,包括最基础的CPU、内存、磁盘、网络的监控,以及Web服务器、JVM、数据库、各类中间件的监控和业务指标的监控。
- 9、灾备演练:类似当前的“混沌工程”,对系统进行一些破坏性手段,观察局部故障是否会引起可用性问题。
高可用的方案主要从冗余、取舍、系统运维3个方向考虑,同时需要有配套的值班机制和故障处理流程,当出现线上问题时,可及时跟进处理。