内容简介:断路器是一种模式,可以防止整个架构中单个微服务的故障级联,从而确保系统具有弹性。该模式可以通过像Hystrix或Resilience4j这样的代码库实现,或者通过底层基础设施来实现,例如使用Istio。
断路器是一种模式,可以防止整个架构中单个微服务的故障级联,从而确保系统具有弹性。该模式可以通过像Hystrix或Resilience4j这样的代码库实现,或者通过底层基础设施来实现,例如使用Istio。
Hystrix vs. Resilience4j简介
Hystrix 是Netflix提供的一个开源库,旨在提高分布式系统的弹性,使HTTP请求在其分布式组件之间进行通信。它通过实现断路器模式实现。
Resilience4J 是一个受Hystrix启发的独立库,它建立在功能编程的原理之上。两者之间最显着的区别在于,虽然Hystrix采用面向对象的设计,其中对外部系统的调用必须包含在HystrixCommand提供多种功能中,但Resilience4J依赖于函数组合来让您堆叠所需的特定装饰器。
那些装饰器当然包括断路器,还包括速率限制器,重试和隔板。这些装饰器可以同步或异步执行,充分利用 Java 8中引入的lambda。
Resilience4J的其他优点包括更精细的配置选项(例如,关闭断路器模式所需的成功执行次数)和更轻的依赖性足迹。
Java中的函数编程简介
函数组合背后的想法是:
- 如果函数f被定义为Function<X, Y>- 将类型X作为输入并返回类型的函数Y
- 如果函数g定义为Function<Y, Z>
- 然后可以将新函数h定义为Function<X, Z>组成函数f和g
Java 8在其API中引入了函数编程(FP)的一些方面。上面的函数组合可以在Java中翻译:
public class F implements Function<Integer, Integer> { @Override public Integer apply(Integer x) { return x + 1; } } public class G implements Function<Integer, Integer> { @Override public Integer apply(Integer y) { return 2 * y; } } public class H implements Function<Integer, Integer> { @Override public Integer apply(Integer x) { var f = new F(); var g = new G(); Integer y = f.apply(x); return g.apply(y); } }
这非常麻烦,因为Java最初设计时考虑了面向对象编程(OOP)。
一切都需要属于一个类,即使这没有多大意义。因此,为了弥合OOP和FP之间的这种差距,并使FP代码更容易编写,Java 8带来了函数接口的概念:功能接口是一个带有单个抽象方法的接口,并且可选择带注释@FunctionalInterface。
可以使用lambda表示法以简化的方式编写任何功能接口。例如,Function<T, V>是一个函数接口,因为它有一个抽象方法 - apply()。因此,可以使用lambdas重写上面的代码:
Function<Integer, Integer> h = x -> { Function<Integer, Integer> f = y -> y + 1; Function<Integer, Integer> g = y -> y + 1; return g.apply(f.apply(x)); };
FP的另一个基础是高阶函数。这只意味着函数是类似于任何其他类型的函数,并且可以作为函数中的参数传递,并作为结果返回。
例如,Functioninterface定义以下方法:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
使用这种方法,我们可以简单地重写h函数:
var h = f.compose(g);
Resilience4J完全基于函数式编程,并且使用了很多公开的概念。重要的是要记住从Hystrix迁移,因为与通常的Java思维方式相比,这需要进行更改。
Resilience4J入门
HTTP调用可以被认为是一个函数:它接受HTTP请求作为输入,并返回HTTP响应。
同样,Circuit Breaker可以被认为是一个函数,输入相同的HTTP请求,返回值如果调用成功则返回HTTP响应,如果失败则返回默认HTTP响应。
因此,使用断路器就像用第二个“断路器”函数组成第一个“调用”函数。
这是一个示例,用于说明如何使用它:
public class Command { public Long run() { // Does the actual job of making the HTTP request } } var cb = CircuitBreaker.ofDefaults("circuit-breaker"); var result = cb.executeSupplier(new Command()::run);
因为Resilience4J中的每个特征都被建模为一个函数,所以组合这些特征只需要应用上述的函数组合原理。
这相当于面向对象编程中的Decorator模式:目标被“包装”到装饰器对象中。
在这里,我们应用此设计来组成三个函数调用。第一个调用HTTP端点,第二个调用Circuit Breaker,第三个调用,如果调用失败则重试。
var cb = CircuitBreaker.ofDefaults("circuit-breaker"); var retry = Retry.ofDefaults("retry"); var cbSupplier = CircuitBreaker.decorateSupplier(cb, new Command()::run); var retrySupplier = Retry.decorateSupplier(retry, cbSupplier); var result = retrySupplier.get();
自定义缓存装饰器
使用Resilience4J实现缓存功能,我们的要求是:只有在修饰函数调用失败时才应从缓存返回。设计我们自己的缓存实现功能非常简单。“函数”这个词很重要,因为根据Resilience4J的设计原则,状态 - 缓存 - 应该是外部的并传递给函数以保持其纯净。为了简化实现,缓存将保留一个值,当装饰函数成功返回时,可能会替换该值:
public class Cache<T> { private T value; static <T> Supplier<T> decorateSupplier(Cache<T> cache, Supplier<T> supplier) { return Try.ofSupplier(supplier) .fold( throwable -> () -> cache.value, value -> { cache.value = value; return () -> value; }); } }
本Try类来自于 Vavr库 ,函数编程API的Java语言和Resilience4J唯一的依赖。它需要两个lambdas:
- 第一个接受一个 Throwable并返回一个返回结果的函数
- 第二个接受该值,并返回一个返回结果的函数
请注意,两者都是惰性的:它们不直接返回结果,而是返回Supplier结果。使用此自定义缓存,现在可以装饰Circuit Breaker调用,以便在电路打开时返回缓存值:
var cb = CircuitBreaker.ofDefaults("circuit-breaker"); var cache = new Cache<Long>(); var cbDecorated = CircuitBreaker.decorateSupplier(cb, new Command()::run); var cacheDecorated = Cache.decorateSupplier(cache, cbDecorated); cacheDecorated.get();
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- SpringCloud学习系列之三----- 断路器(Hystrix)和断路器监控(Dashboard)
- 用断路器驯服数据质量
- SpringCloud 断路器(Hystrix)
- 云设计模式之: 断路器模式
- Java EE的断路器API设计
- 微服务断路器Istio与Hystrix比较
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。