不可否认,在过去几年中,像Docker和Kubernetes这样的技术,彻底改变了我们对软件开发和部署的方式。
尽管软件开发行业的快节奏推动开发人员使用新技术,但是重要的是我们须要退一步思考并理解这些技术的背后原理及模式。
断路器模式就是在微服务架构中广泛采用的模式之一。 我们将比较实现它的两种不同方法的优缺点:Hystrix和Istio。
图片.png 微服务同步通信的核心问题
假设有一个非常简单的微服务架构,包括: 1.后端服务 2.前端服务(注:可以理解成是微服务A,B两个服务,A调用B服务,这里的前端服务就是A)
我们假设后端和前端通过同步HTTP调用进行通信。
客户端C1和C2调用前端来获取一些信息。由于前端没有所有必需的数据,因此它会调用后端来获取缺失的数据。
但是由于网络通信会产生很多问题:
-
前后端的网络故障
-
后端由某个bug导致服务挂了
-
后端依赖的服务(例如数据库)挂了
根据墨菲定律(“任何可能出错的东西都会出错”),前端和后端之间的通信早晚会遇到失败的。
如果我们从单个请求从前端到后端的生命周期来看,后端服务会由于各种各样的原因导致不可用,在某些时候,前端服务调用会由于超时而取消。
缩小到应用程序级别,多个客户端同时调用前端服务,这实际变成了对后端服务的多次调用:前端服务的很多请求将处于超时状态。
在这种情况下唯一合理的解决方案是快速失败:应该让前端服务知道后端服务出现问题,并立即将故障返回给自己的客户端。
断路器模式
在电路领域中,断路器是设计用于保护电路的自动操作的电气开关。 其基本功能是在检测到故障后中断电流。 然后可以在故障解决后重置(手动或自动)以恢复正常操作。
这和我们的问题非常的类似:为了保护我们的应用程序免受过多的请求,最好在后端检测到故障时立即中断前端和后端之间的通信。
在他已发布的书中,迈克尔·尼加德使用了这个类比,并提出了应用于上述超时问题的设计模式。 它背后的流程非常简单:
-
如果调用失败,将失败值的增加1
-
如果调用失败的次数超过某个阈值,就打开电路
-
如果电路处于打开状态,则立即返回错误信息或返回一个默认值
-
如果电路处于打开状态且已经过了一段时间,则半开电路
-
如果电路半开,下一次请求调次失败,请再次打开
-
如果电路半开,下一次请求调用成功,请将其关闭
我们可以用下图来进行总结:
图片.png Istio断路器
Istio是服务网格,是微服务应用程序的可配置基础设施层。 它使服务实例之间的通信变得灵活,可靠和快速,并提供服务发现,负载平衡,加密,身份验证和授权,对断路器模式的支持以及其他功能。
Istio是在底层集群管理平台上提供了一个抽象层,例如Kubernetes,Mesos等,并且需要以这种方式管理您的应用程序。
其核心,Istio使用(sidecar container pattern)集装箱模式并位于应用程序实例前面的Envoy代理实例,以及管理它们的工具Pilot。这种代理策略有许多优点:
- HTTP,gRPC,WebSocket和TCP流量的自动负载均衡。 2.通过丰富的路由规则,重试,故障转移和故障注入,对流量行为进行细粒度控制。 3.可插入的策略层和配置API,支持访问控制,速率限制和配额。 4.集群中所有流量的自动度量(metrics),日志和跟踪,包括群集入口和出口。 5. 基于强大的身份的身份验证和授权,在集群中实现服务到服务安全的通信。
由于后端的出站调用是通过Envoy代理,因此很容易检测到它们何时超时。然后代理可以拦截进一步的调用并立即返回,有效地快速失败。特别是,这使得断路器图能够以黑盒方式操作。
配置Istio断路器
正如我们所说,Istio在您选择的集群管理平台上构建,并且需要通过它来部署您的应用程序。 Kubernetes通过DestinationRule实现断路器模式,更确切的路径就是TrafficPolicy(以前叫circuitBreaker) - > OutlierDetection,模型如下:
图片.png参数如下:
Parameters are as follows:
Field Description
consecutiveErrors 当断路器开起的时候,返回5XX的错误码
interval 断路器检查分析(check analysis)之间的时间间隔。
baseEjectionTime 最短开放时间
maxEjectionPercent Maximum % of hosts in the load balancing pool for the upstream service that can be ejected.
与前面讲的断路器相比,有两个主要差异:
-
半开状态是不存在的。但是,断路器保持打开的持续时间取决于被调服务之前失败的次数。不断失效的服务将导致断路器的打开持续时间越来越长。
-
在基础模式中,有一个叫做应用程序(后端)。在现实的生产环境中,可能会在负载均衡器后面部署同一应用程序的多个实例。有些实例可能会失败,有些可能会正常工作,而且由于Istio也扮演了负载平衡器的角色,它能够跟踪失败的实例并将它们从负载均衡池中弹出,在某种程度上说:maxEjectionPercent attribute作是保留一部分实例在池中。
Istio断路器更像是一个黑盒子。它需要一个高视角,并且只能在出现问题时打开电路。另一方面,它设置起来非常简单,并且不需要任何底层代码知识,并且可以将其配置为事后想法。
Hystrix断路器
Hystrix是一个最初由Netflix提供的开源Java库。 它是一个延迟、容错库,旨在隔离对远程系统,服务和第三方库的访问时,避免级联故障,并在复杂的分布式系统中实现弹性,因为在这些系统中,故障是不可避免的。
Hystrix有许多功能,包括: 通过第三方客户端库访问(通常通过网络)访问的依赖项,防止延迟和故障。 防止复杂分布式系统中的级联故障。 快速失败并迅速恢复。 在可能的情况下,实施优雅地降级。 实现近实时监控,报警和操作控制。 当然,断路器模式在这些功能中占有一席之地。 因为Hystrix是一个库,它以白盒方式实现它。
注意:
Resilience4J Netflix最近宣布已停止开发Hystrix库,转而采用不太知名的Resilience4J项目。 尽管客户端代码可能有点不同,但Hystrix和Resilience4J之间的方法是类似的。
Hystrix断路器例子
考虑电子商务Web应用程序的情况。 该应用程序由不同的微服务构成,每个服务都基于业务功能划分: 认证 目录浏览 购物车管理 定价和报价 等等。
当显示目录的时候,将通过定价和报价服务查询其定价/报价。如果这个服务挂掉了,他将不会返回任务价格回来,就会导致无法购买任何东西。
从商业角度来看,宕机不仅会对品牌的感知产生影响,还会降低销售额。 尽管价格并不完全正确,但大多数销售策略仍倾向于出售。 实现此销售策略的解决方案是:在服务不可用的时候可以返回缓存里的价格。
Hystrix提供了一种断路器实现:允许在电路打开时进行降级。
这是Hystrix模型的简化类图:
图片.png
最核心的地方发生在HystrixCommand的 run()和getFallback()方法中: run()是实际代码,例如 从报价服务中获取价格 当断路器打开时,getFallabck()获取降级结果,例如返回缓存价格 这可以使用Spring的RestTemplate转换为以下代码:
图片.png
我们对上面的例子做一些解释: 1.该命令封装产品的id,使用的是UUID。 2.Spring的RestTemplate用于进行REST调用。任何其他选择都可以。 3.共享Cache实例,用于在服务可用时缓存产品的价格。 4.Hystrix命令需要组密钥,以便在需要时将它们组合在一起。 这是Hystrix的另一个特性,超出了本文的范围。有兴趣的读者可以阅读Hystrix wiki中的命令组。 5.执行对价格服务的调用.如果调用失败,则启动Hystrix断路器的fall back。 6.如果调用成功,则将返回的引用缓存在Cache实例中的值。 7.断路器打开时调用getFallback()。 在这种情况下,从缓存中获取价格。 Hystrix wiki具有更高级的示例,例如 fallback本身就是一个需要执行的命令。
Spring Cloud和Hystrix集成
虽然上面的代码有效,但每次引用时都需要创建一个Hystrix命令对象。 Spring Cloud是一个构建在Spring Boot之上的库(它本身构建在Spring框架之上),它提供了与Spring的完美集成。 它允许在处理Hystrix命令对象的实例化时,只添加注解在所需的降级方法上面:
图片.png-
须要在该方法上添加@HystrixCommand](mailto:1.须要在该方法上添加@HystrixCommand)。里面的
fallbackMethod``引用了
fallback方法。当然,这将通过反射来处理,并且不是类型安全的 - 毕竟这是一个字符串。 -
Spring Cloud Hystrix允许在方法调用时传递产品的id参数。 与上面的简单Hystrix命令相比,这允许具有通用服务对象。 Hystrix命令的创建由Spring Cloud在运行时处理。
-
核心逻辑不会改变
-
同样,缓存处理保持不变。
-
fallback方法是一个普通方法。它将使用与主方法完全相同的参数值进行调用,因此它必须具有相同的参数类型(以及相同的顺序)。 因为getQuoteFor()方法接受UUID,所以此方法也是如此。
Hystrix,无论是独立的还是由Spring Boot Cloud包装,都需要在代码级别处理断路器。 因此,它需要提前计划好,并且更改需要部署更新的二进制文件。 但是,当出现问题时,可以实现非常精细的定制行为。
Istio 和 Hystrix:断路器之争
如果事情有可能失败,给定时间,事情将会失败,并且严重依赖网络的微服务需要设计为失败。断路器模式是处理服务可用性不足的方法之一:它不是排队请求和阻塞调用者,而是快速失败并立即返回。
有两种方法可以实现断路器,黑盒方式和白盒方式。 Istio作为代理管理工具,采用黑盒方式。它实现起来很简单,它不依赖于底层技术堆栈,而且它可以事后添加配置信息。
另一方面,Hystrix库使用白盒方式。它允许拥有所有不同类型的fallbacks: 单一的默认值 缓存 调用其他服务
它还提供级联降级(fallback)。这些附加功能需要付出代价:它需要在仍处于开发阶段时做出降级决策。 两种方法之间的最佳匹配可能取决于不同的场景:在某些情况下,例如引用服务,带有降级的白盒策略可能更适合,而对于其他情况,快速失败可能完全可以接受,例如集中式远程日志服务。 当然,你也可以同时使用它们。
原文:https://www.exoscale.com/syslog/istio-vs-hystrix-circuit-breaker/
网友评论