架构设计04--设计原则02--快速失败
架构设计系列文章,请参见连接。
背景
软件研发过程中一个根本性的问题:为什么要做软件(架构)设计?在软件开发过程中软件设计可以分为两个方面:业务设计,技术设计。而业务设计关系着业务能力,业务服务内容,所以业务设计是必须的。也就是软件业务设计定义了软件要解决现实生活中的问题,如果没有这个问题我们也不需要去研发软件了。而对于软件设计来说技术设计是不是就变成一个可有可无的东西?
在大多数软件团队中都处于没有技术设计的阶段。而这样的阶段就会形成它独特的特点:
-
混乱无序
模块(服务)间关系混乱,服务的分层关系混乱,以业务为中心的代码直接将业务的复杂度与代码耦合过深导致从业务的混乱影响代码的复杂度。 -
没有技术目标
新的业务应该写在那个模块中,无谓的服务划分规则。不制定技术规范无法对于将来的变化做充分的应对方案。 -
不可扩展
每行代码都在处理业务,业务与业务之间纠缠的直接反应在代码上。导致一套代码只能适用于这个特定的业务,根本无法在其他地方复用。业务之间的纠缠导致代码的可修改行极度的差。
这几个特点对业务来说是致命的。严重的影响着业务的持续发展能力,不能支撑业务的发展。这是一种因为对于技术的忽略而反推到对业务发展的不负责的一种思路。因为技术是支撑业务的,但是在支撑业务过程中不考虑架构的事情就会反响的影响业务的持续发展。
而快速失败就是一个能够体现技术架构设计影响业务发展的一个例子。下面具体说明快速失败用怎样的方式影响业务的发展。
快速失败的层次
对于软件系统来说,系统化的管理是必不可少的。而快速失败也体现在系统化管理的各个层面上。这里我将应用快速失败的系统化管理的层面分为:业务规划层,系统层,服务间通信层,代码层。在每一层中都有它独特的快速失败方法。而在不同的层面间共享的Fail-fast原则有着一些共同的特点。
在Wiki上对于快速失败的定义:
在系统设计中,Fail-fast系统是指在接口处立即报告任何可能指示故障的情况的系统。Fail-fast系统通常设计为停止正常运行,而不是试图继续一个可能有缺陷的过程。这样的设计经常在一个操作中的几个点检查系统的状态,因此任何故障都可以早期检测到。Fail-fast模块的职责是检测错误,然后让系统的下一个最高级别处理错误。
它们的共同点就是在边界处进行处理并报错。以最快的速度响应,以提高性能与可靠性。
How to Works?对于快速失败后,问题应该怎样解决?上图为在发生快速错误时的解决方案,在快速失败的情况去给各方一个快速反馈。以快速反馈的方式反馈出错误原因,这样可以更有针对性的进行问题解决工作。
-
业务规划层
在业界MVP的概念就是在业务规划层面上对于Fail-fast策略的最大体现。这意味着企业可以通过进行大胆的实验,以确定产品或战略的长期可行性,而不是谨慎行事,在注定要失败的产品、战略上投资数年。它成为了创业文化中的一种经典实践。
-
系统层
不允许将错误动作、异常数据在系统中传递,导致系统整体数据的污染。如果允许传播错误动作,异常数据,那么就会将错误传递到整个系统,从而影响系统的整体稳定性。而在系统层面上保证系统的可靠性和性能优越性,那么就必须让系统满足快速失败原则。
-
微服务层
任何的第三方都是不可信的。在这个过程中需要做到:怀疑第三方,防备使用方,做好自己。这样才可以防止故障,错误在微服务间传递。以快速检测、快速发现的方式检测到第三方的问题,并快速报错。并限制服务间下一次重试的时间,以最大限度的降低系统的不稳定性。
-
代码层
有人参与的地方都不能保证不出错,而开发人员编写的代码肯定也是会出错的。那么尽快的检测出错误并修复就成为了当务之急,所以在每个接口的第一部分就是需要业务层面合法性的判断。而且还需要在接下来的技术层面上的代码错误也尽快返回,以防止用户请求阻塞。技术错误导致资源(线程,内存,数据库)被占用的情况。
技术
上面基本上已经说明了在不同的层面上需要哪些快速失败的方法。这里主要进行快速失败技术方面的讨论。针对技术层面上可能需要考虑负载均衡器的快速失败,服务健康度检测技术,参数验证框架等内容。
实践
-
Nginx
在Nginx上可以设置proxy_connect_timeout
来标示最长可以接收的连接超时时间,如果服务超过这个时间就可以直接报错。以保证系统快速反馈的能力。可以设置upstream
的keepalive
和limit_req_zone
、limit_req
来控制流量。以达到快速失败的目的。 -
Spring retry
spring-retry在分布式系统中,为了保证数据分布式事务的强一致性,大家在调用RPC接口或者发送MQ时,针对可能会出现网络抖动请求超时情况采取一下重试操作。大家用的最多的重试方式就是MQ了,但是如果你的项目中没有引入MQ,那就不方便了,本文主要介绍一下如何使用Spring Retry实现重试操作。
Spring retry是使用aop的方式监听系统内发生的错误,并以重试的机制进行重试。与Spring Boot集成时使用@EnableRetry
注解来启动重试服务。以代码形式的配置为:
RetryTemplate template = new RetryTemplate();
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(30000L);
template.setRetryPolicy(policy);
Foo result = template.execute(new RetryCallback<Foo>() {
public Foo doWithRetry(RetryContext context) {
// Do stuff that might fail, e.g. webservice operation
return result;
}
});
-
Netflix Hystrix
Netflix Hystrix通过将依赖服务进行资源隔离,进而组织某个依赖服务出现故障的时候,这种故障在整个系统所有的依赖服务调用中进行蔓延,同时Hystrix还提供故障时的fallback降级机制。而Fail-fast就是一种降级机制。 -
Sentinel
Sentinel: 分布式系统的流量防卫兵,是阿里中间件团队2018年7月开源的,面向分布式服务架构的轻量级流量控制产品,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来保护系统服务的稳定性
Sentinel针对指定应用实例的流量控制,监控应用流量QPS或并发线程数,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。可以支持线上动态限流。
总结
快速失败对于架构设计来说是一个需要遵循的原则。不过也可以将快速失败原作推而广之到生产、生活的各个方面,指导我们的日常工作、生活。在于技术层面快速失败可以为软件系统的可靠性,性能方面提供很好的支撑作用。可以在不同的软件层面践行这个原则。
参考:
快速失败的能力
构建可靠系统的原则与实践
构建可靠的系统
云原生架构及设计原则
监控什么?4个黄金指标/RED方法/USE方法
突破Java面试-hystrix分布式系统可用性及设计原则
如何健壮你的后端服务?
性能-快速失败与稳健性
Wikipedia:Fail-fast