微服务断路器Istio与Hystrix比较

18-12-14 banq
                   

无可否认,在过去几年中,像Docker和Kubernetes这样的技术,彻底改变了我们对软件开发和部署方式。断路器模式是在微服务架构中广泛采用的那些模式之一。我们将比较实现它的两种不同方法的优缺点:Hystrix和Istio。

微服务同步通信的核心问题

想象一个非常简单的微服务架构,包括:

  1. 后端服务
  2. 前端服务

让我们假设后端和前端通过同步HTTP调用进行通信。

客户端C1并C2调用前端来检索一些信息。由于前端没有所有必需的数据,因此它会调用后端来获取缺失的部分。

但由于网络通信,可能会发生很多事情:

  • 前端和后端之间出现网络故障
  • 后端可能是因为一个错误的宕机
  • 后端服务依赖的数据库宕机

而按照墨菲定律(“凡是可能出错就会出错”),前端和后端之间的通信失败是迟早的事。

在这种情况下唯一合理的解决方案是快速失败: 应该让前端知道后端出现问题,并立即将故障返回给自己的客户端。

断路器模式

在电路领域中,断路器是设计用于保护电路的自动操作的电气开关。其基本功能是在检测到故障后中断电流。然后可以在故障解决后重置(手动或自动)以恢复正常操作。

应用于上述超时问题的设计模式。它背后的流程非常简单:

  • 如果呼叫失败,请将失败呼叫的数量增加1
  • 如果失败的呼叫数超过某个阈值,请打开电路
  • 如果电路处于打开状态,则立即返回错误或默认响应
  • 如果电路开路且经过一段时间,则半开电路
  • 如果电路半开,下一次呼叫失败,请再次打开
  • 如果电路半开,下一次通话成功,请将其关闭

Istio断路器

Istio是服务网格,是微服务应用程序的可配置基础设施层。它使服务实例之间的通信变得灵活,可靠和快速,并提供服务发现,负载平衡,加密,身份验证和授权,对断路器模式的支持以及其他功能。

Istio的控制平面在底层集群管理平台上提供了一个抽象层,例如Kubernetes,Mesos等,并且需要以这种方式管理您的应用程序。

作为其核心,Istio使用边车容器模式:这是一个位于应用程序实例前面的Envoy代理实例,以及管理它们的工具Pilot。这种代理策略有许多优点:

  • HTTP,gRPC,WebSocket和TCP流量的自动负载平衡。
  • 通过丰富的路由规则,重试,故障转移和故障注入,对流量行为进行细粒度控制。
  • 可插入的策略层和配置API,支持访问控制,速率限制和配额。
  • 群集中所有流量的自动度量标准,日志和跟踪,包括群集入口和出口。
  • 通过强大的基于身份的身份验证和授权,在群集中实现安全的服务到服务通信。

由于对后端的出口调用通过Envoy代理,因此很容易检测到它们何时超时。然后代理可以拦截进一步的调用并立即返回,有效地快速失败。特别是,这使得断路器图案能够以黑盒方式操作。

配置Istio断路器

正如我们所说,Istio在您选择的集群管理平台上构建,并且需要通过它来部署您的应用程序。根据以下模型,Kubernetes 实现断路器模式:通过一个 DestinationRule或更具体的路径策略TrafficPolicy调用 - >OutlierDetection:

参数如下:

consecutiveErrors: 在断路器打开前的返回5xx的数量。

interval:断路器检查分析之间的时间间隔。

baseEjectionTime:最短打开时间。电路将保持等于最小喷射持续时间和断路打开次数的乘积。

maxEjectionPercent:负载平衡池中可以弹出的上游服务的最大主机百分比。

与前面标准的断路器相比,有两个主要偏差:

  • 没有半开状态这样的事情。但是,断路器保持打开的持续时间取决于被叫服务之前失败的次数。不断失效的服务将导致断路器的打开持续时间越来越长。
  • 在基本模式中,有一个叫做应用程序(后端)。在更现实的生产设置中,可能会在负载均衡器后面部署同一应用程序的多个实例。有些实例可能会失败,有些可能会失效,而且由于Istio也扮演负载均衡器的角色,它能够跟踪失败的实例并将它们从负载平衡池中弹出删除,直到某一点:maxEjectionPercent的角色属性将保留实例池中的一小部分。

断路器Istio接近是黑盒子。它需要一个高视点支架,并且只能在出现问题时打开断路。另一方面,它设置起来非常简单,并且不需要任何底层代码知识,并且可以将其配置为事后想法。

Hystrix断路器

Hystrix是一个最初由Netflix提供的开源Java库。它是一个延迟和容错库,旨在隔离对远程系统,服务和第三方库的访问点,停止级联故障,并在复杂的分布式系统中实现弹性,在这些系统中,故障是不可避免的。

Hystrix有许多功能,包括:

  • 通过第三方客户端库访问(通常通过网络)访问的依赖项,防止延迟和故障。
  • 防止复杂分布式系统中的级联故障。
  • 快速失败并迅速恢复。
  • 在可能的情况下,后退并优雅地降级。
  • 实现近实时监控,警报和操作控制。

当然,断路器模式在这些功能中占有一席之地。因为Hystrix是一个库,它以白盒方式实现它。

Resilience4J

Netflix最近宣布已停止开发Hystrix库,转而采用不太知名的Resilience4J项目。

即使客户端代码可能有点不同,Hystrix和Resilience4J之间的方法也是类似的。

Hystrix断路器示例

考虑电子商务Web应用程序的情况。该应用程序的体系结构由不同的微服务构成,每个服务都基于业务功能:

  • 认证
  • 目录浏览
  • 购物车管理
  • 定价和报价
  • 等等。

显示目录项时,将查询定价/报价微服务的价格。如果中间通信断开,将不会发回任何价格,也无法订购任何东西。

从商业角度来看,任何停机时间不仅会对品牌的感知产生影响,还会降低销售额。尽管价格并不完全正确,但大多数销售策略仍倾向于出售。实现此销售策略的解决方案可以是在可用时缓存定价/报价服务返回的价格,并在服务停止时返回缓存价格。

Hystrix通过提供断路器实现允许该方法,该电路断路器实现允许在电路断开时进行回退。

这是Hystrix模型的简化类图:

魔术发生在HystrixCommand方法run()和getFallback():

  • run()是实际代码,例如从报价服务中获取价格
  • getFallabck()当断路器打开时获得后备结果,例如返回缓存价格

这可以转换为以下代码,使用Spring RestTemplate:

public class FetchQuoteCommand extends HystrixCommand<Double> {

    private final UUID productId;                                               // 1
    private final RestTemplate template;                                        // 2
    private final Cache<UUID, Double> cache;                                    // 3

    public FetchQuoteCommand(UUID productId,
                             RestTemplate template,
                             Cache<UUID, Double> cache) {
        super(HystrixCommandGroupKey.Factory.asKey("GetQuote"));        // 4
        this.template = template;
        this.cache = cache;
        this.productId = productId;
    }

    @Override
    protected Double run() {
        Double quote = template.getForObject("https://acme.com/api/quote/{id}", // 5
                                             Double.class,
                                             productId);
        cache.put(productId, quote);                                            // 6
        return quote;
    }

    @Override
    protected Double getFallback() {
        return cache.get(productId);                            // 7
    }
}

这需要一些解释:

  1. 该命令包装了产品的id,模仿为a UUID。
  2. Spring RestTemplate用于进行REST调用。任何其他选择都可以。
  3. 共享JCache实例,用于在服务可用时存储引号。
  4. Hystrix命令需要组密钥,以便在需要时将它们组合在一起。这是Hystrix的另一个特性,超出了本文的范围。有兴趣的读者可以阅读Hystrix wiki中的命令组。
  5. 执行对引用服务的调用。如果失败,则启动Hystrix断路器流量。
  6. 如果调用成功,则将返回的引用缓存在JCache共享实例中。
  7. 在getFallback()当断路器打开时调用。在这种情况下,从缓存中获取引用。

Hystrix wiki具有更高级的示例, 例如 后备本身就是一个需要执行的命令。

(banq注:更简单Springcloud应用:)

@HystrixCommand(fallbackMethod = "createProduct")
 public Product getProduct(@RequestParam String productId) {
      ...
    }

private Product createProduct(String productId) {
        Product product = new Product();
        product.setId("999999");
        product.setName("网络问题");
        return product;

    }

将Hystrix与Spring Cloud集成

虽然上面的代码有效,但每次引用时都需要创建一个Hystrix命令对象。

Spring Cloud是一个构建在Spring Boot之上的库(它本身构建在Spring框架之上),它提供了与Spring的完美集成。它允许在处理Hystrix命令对象的实例化时,只注释所需的回退方法:

public class FetchQuoteService {

    private final RestTemplate template;
    private final Cache<UUID, Double> cache;

    public SpringCloudFetchQuoteCommand(RestTemplate template,
                                        Cache<UUID, Double> cache) {
        this.template = template;
        this.cache = cache;
    }

    @HystrixCommand(fallbackMethod = "getQuoteFromCache")                       // 1
    public Double getQuoteFor(UUID productId) {                                 // 2
        Double quote = template.getForObject("https://acme.com/api/quote/{id}", // 3
                                             Double.class,
                                             productId);
        cache.put(productId, quote);                                            // 4
        return quote;
    }

    public Double getQuoteFromCache(UUID productId) {                           // 5
        return cache.get(productId);
    }
}

  1. 该方法应注明@HystrixCommand。该fallbackMethod元素引用了回退方法。显然,这将通过反射来处理,并且不是类型安全的 - 毕竟这是一个字符串。
  2. Spring Cloud Hystrix允许在方法调用时传递产品的id参数。与上面的简单Hystrix命令相比,这允许具有通用服务对象。Hystrix命令的创建由Spring Cloud在运行时处理。
  3. 核心逻辑不会改变。
  4. 同样,缓存过程保持不变。
  5. 后备方法是常规方法。 它将使用与main方法完全相同的参数值进行调用,因此它必须具有相同的参数类型(以相同的顺序)。因为该getQuoteFor()方法接受UUID,所以此方法也是如此。

Hystrix,无论是独立的还是由Spring Boot Cloud包装,都需要在代码级别处理断路器。因此,它需要提前计划,并且更改需要部署更新的二进制文件。但是,当出现问题时,可以实现非常精细的定制行为。

Istio vs Hystrix:断路器之战

断路器模式是处理服务可用性不足的方法之一:它不是排队请求和阻塞调用者,而是快速失败并立即返回。

有两种方法可以实现断路器,黑盒方式和白盒方式。Istio作为代理管理工具,采用黑盒方式。它实现起来很简单,它不依赖于底层技术堆栈,它可以被配置为事后的想法。

另一方面,Hystrix库使用白盒方式。它允许拥有所有不同类型的后备:

  • 单个默认值
  • 缓存
  • 呼叫其他服务。

它还提供级联回退。这些附加功能需要付出代价:它需要在仍处于开发阶段时做出后退决策。

两种方法之间的最佳匹配可能取决于一个人自己的背景:在某些情况下,例如引用服务,带有后备的白盒策略可能更适合,而对于其他情况,快速失败可能完全可以接受,例如集中式远程日志服务。

 

                   

2