带Hystrix的断路器模式

前文已经讨论过Spring Cloud Nttlix 中负载均衡器算法的不同实现。其中一些基于监视实例响应时间或失败次数。在这些情况下,负载均衡器会根据这些统计信息决定应该调用哪一个实例。 断路器模式(Circuit Breaker Patterm)应该被视为该解决方案的扩展。断路器背后的主要思想非常简单,受保护的函数调用将包含在断路器对象中,该对象负责监视故障调用的数量。如果故障达到阈值,则电路断开,所有其他调用将自动失败。通常情况下,如果断路器跳闸,则开发人员会希望有某种监控警报。在应用程序中使用断路器模式所带来的一些重要好处是:应用程序能够在相关服务发生故障时继续运行,防止出现级联故障,并且能够给予服务恢复的时间。

不同微服务之间如何认证_java

 

使用 Hystrix 构建应用程序

Netlix在其库中提供了一个名为Hystrix的断路器模式实现。该库也被包含在SpringCloud的断路器的默认实现中。Hytrix 还有- -些其他有趣的功能,也应该被视为处理分布式系统的延迟和容错的综合工具。重要的是,如果断路器被断开,则Hystrix会将所有调用重定向到指定的回退方法( Fallback Method) 。回退方法旨在提供通用响应,而不需要依赖网络(一般来说,会从内存缓存中读取响应或仅实现为静态逻辑)。如果因为某些原因必须执行网络调用,则建议使用其他的HystrixCommand或HystrixObservableCommand实现它。要在项目中包含Hystrix, 则应该为Spring Cloud Netlix (请注意,必须是早于1.4.0 .的版本)使用spring clou-starter ntitx-hytrix或spring-cloud- sarter-hystix启动器。

<dependency>

<groupId>org. springfr amework. cloud</groupId>

<artifactId>spring-cloud-starter-hystrix</artifactId>

</dependency>

1.实现Hystrix的命令

Spring Cloud Netlix Hystrix将查找使用@HystrixCommand进行注解的方法,然后将其包裹在连接到断路器的代理对象(Proxy Object)中。由于这个原因,Hytrix 能够监控这种方法的所有调用。此注解目前仅适用于标有@Component或@Service的类。这对于我们来说是很重要的信息,因为我们已经在REST控制器类中实现了与所有先前示例中调用的其他服务相关的逻辑,该控制器类采用的标记是@RestController注解。因此,在customer-service服务应用程序中,所有逻辑都已移至新创建的CustomerService 类,然后该类将被注入控制器bean。 负责与account-service 服务通信的方法已使用@HystrixCommand进行了注解。此外,我们还实现了一个回退方法,其名称将传递到fllbackMethod注解的字段中。此方法仅返回一个空列表。

@Service
public class CustomerService {
@Autowired
RestTemplate template;
@Autowired
CustomerRepository repository;
// ...
@HystrixCommand (fallbackMethod = "findCustomerAccountsFallback")
public List<Account> findCustomerAccounts (Long id) {
Account [ ] accounts =
template.getForobject ("http://account-service/ customer/ {customerId)",
Account[] .class, id) ;
return Arrays. stream (accounts) . collect (Collectors. toList() );
}
public List<Account> findCustome rAccountsFallback(Long id) {
return new ArrayList<>() ;
}
}

不要忘记用@EnableHystrix标记main类,因为它会告诉Spring Cloud, 应该为应用程序使用断路器。当然,也可以选择使用@EnableCircuitBreaker来注解一个类, 它的作用是一样的。出于测试目的,acount-serviceribbon. listOfServers属性应该包含localhost:8091和localhost:9091服务的两个实例的网络地址。

不同微服务之间如何认证_面试_02

 

虽然我们已经为Ribbon客户端声明了account-service 服务的两个实例,但是在这里我们将仅启动8091端口上可用的实例。如果调用customer-service, 服务方法
GEThtp://calhost:8092/withAccounts/ {id},则Ribbon将尝试对这两个已声明的实例之间的每个传入请求进行负载均衡,也就是说,一旦接收到一个包含账户列表的响应,那么第二次将收到一个空账户列表,反之亦然。这在下面的应用程序日志片段中可以看得很清楚。要访问该示例应用程序的源代码,开发人员需要切换到与第6章中的示例相同的GitHub存储库的hystrix_ basic 分支tps:/github.com/piomin/sample-spring cloud- comm/tree/hystrix_ basic) 。

{"id":1, "name":"John Scott" ,"type" : "NEW" , "accounts":[]}
{"id":1, "name": "John
Scott","type": "NEW","accounts":[{"id":1, "number" :"1234567890",
"balance" :5000}, {"id":2,"number" :"1234567891" , "balance" :5000},
{"id":3,”number":"1234567892", "balance":0}1}

2.使用缓存数据实现回退

上一个示例中提供的回退实现非常简单。返回空列表对于在生产模式中运行的应用程序没有什么实际意义。反之,在应用程序中使用回退方法则更有意义,例如,当请求失败时,可以从缓存中读取数据。像这样的缓存可以在客户端应用程序内部实现,或者也可以使用第三方工具实现,如Redis、Hazelcast或EhCache。在Spring Framework中也提供了最简单的实现,并且在将spring-boot-starter-cache工件与依赖项一起包含之后即可使用。要为Spring Boot应用程序启用缓存,应该使用@EnableCaching注解main或配置类,并在以下上下文环境中提供CacheManager bean。

@SpringBootApplication
@RibbonClient (name = "account -service")
@EnableHystrix
@EnableCaching
public class CustomerApplication {
OLoadBa lanced
@Bean
RestTemplate restTemplate() (
return new RestTemplate() ;
}
public static void main(String[] args) {
new
SpringApplicat ionBui lder (CustomerApplication.class) .web (true) .run(args);
}
@Bean
public CacheManager cacheManager (){
return new ConcurrentMapCacheManager ("accounts");
//...
}

然后,可以使用@CachePut 注解来标记被断路器包裹(Wrap) 的方法,这将把从调用方法返回的结果添加到缓存映射(Cache Map)。在这种情况下,该映射被命名为accounts。最后,可以通过直接调用CacheManager bean来读取回退方法实现中的数据。如果多次重试相同的请求,则开发人员将看到空的账户列表不再作为响应返回。相反,该服务将始终返回在第一次成功调用期间缓存的数据。

@Autowi red
CacheManager cacheManager;
@CachePut ("accounts")
@HystrixCommand (fallbackMethod =”findCustomerAccountsFallback")
public List<Account> findCustomerAccounts (Long id) {
Account[] accounts =
template. getForobject ("http://account-service/ customer/ (customerId)",
Account[] .class; id) ;
return Arrays .stream (accounts) .collect (Collectors. toList());
}
public List<Acount> findCustomerAccountsFallback (Long id) (
ValueWrapper w - cacheManager 。getCache ("accounts").get (id);
if (W != null) {
return (List<Account>) w.get() ;
} else {
return new ArrayList<>();
}
}

跳闸断路器

现在不妨来做一个练习。 截至目前,开发人员已经学习了如何使用Hystrix与SpringCloud一起在应用程序中启用和实现断路器,以及如何使用回退方法从缓存中获取数据。但是,仍然没有使用跳闸断路器来防止负载均衡器调用故障的实例。现在,如果故障百分比大于30%,则我们想要配置Hystrix在3次失败的调用尝试之后断开电路,并防止在接下来的5秒内调用API方法。测量时间窗口约为10秒。为了满足这些要求,开发人员必须覆盖若千个默认的Hystrix 配置设置。它可以使用@HystrixCommand中的@HytrixProperty注解来执行。

不同微服务之间如何认证_面试_03

 

以下是负责从customer- service服务获取账户列表的方法的当前实现。

@CachePut ("accounts")
@HystrixCommand(fallbackMethod - "findCus tome rAccountsFallback",
commandProperties = {
@HystrixProperty (name =
"execution. isolation. thread. timeoutInMilliseconds", value = "500"),
@HystrixProperty(name = "circuitBreaker . requestVolumeThreshold" ,
value= "10"),
@HystrixProperty (name = "circuitBreaker .errorThresholdPercentage" ,
value = "30"),
@HystrixProperty (name - "circuitBreaker. sleepWindowInMilliseconds",
value = "5000"),
@HystrixProperty(name = "metrics. rollingStats.timeInMilliseconds",
value = "10000")
public List<Account> findCustomerAccounts (Long id) {
Account[] accounts =
template. getForobject ("http://account-service/ customer/ (customerId}",
Account[] .class; id) :
return Arrays.stream (accounts) .collect (Collectors.toList();

有关Hystrix配置属性的完整列表,请访问Nettlix的GitHub站点
tps:/ihub.com/Netlix/Hystrix/wikiConfiguration)。本章不会详细讨论所有这些属性,而只是关注对于微服务之间的通信来说最为重要的属性。以下是我们的示例中使用过的属性的列表及其说明。

口execution. 
isolation.thread.timeoutlnMilliseconds:此属性将设置以毫秒为单位的时间,在此时间之后将发生读取或连接超时,客户端将终止命令执行。Hystrix 将这种方法调用标记为失败,并执行回退逻辑。通过将command timeout.enabled属性设置为false,可以完全关闭该超时设置。默认值为1000毫秒。

口 
circuitBreaker.requestVolumeThreshold: 此属性可以设置滚动窗口中将使电路跳闸的最小请求数。默认值为20。在我们的示例中,此属性被设置为10,这意味着前9次申请即使全部失败也不会使电路跳闸。请注意,在设置了该值之后,因为前面已经假设如果30%的传入请求失败,就应该断开电路,所以最小的传入请求数其实是3。


circuitBreaererrorThresholdPercentage:此属性可以设置最小错误百分比,超过此百分比会导致断开电路,系统开始短路请求回退逻辑。默认值为50。在上面的示例中已经将其设置为30,因为我们希望在有30%的请求失败的情况下即断开电路。

口circuitBreaker. sleepWindowInMillseconds:此属性设置跳闸电路和允许尝试以确定是否应再次断开电路之间的一段时间。在此期间,所有传入的请求都将被拒绝。默认值为5000(毫秒)。因为我们想在电路断开后第一次重新尝试调用之前等待10秒,所以将其设置为10000。


metris.ollingStats.timelnMilliseconds:此属性可以设置统计滚动窗口的持续时间(以毫秒为单位)。这是Hystrix为断路器的使用和发布而保留的指标(Metrics)时间。

通过这些设置,开发人员可以运行与前一个示例相同的JUnit 测试。我们将使用HoverflyRule启动两个account-service 服务存根。其中第一个将延迟200毫秒,而第二个将延迟2000毫秒,该值大于使用
execution.isolation.thread. timeoutInMilliseconds属性为@HystrixCommand设置的超时(默认为1000毫秒)。

在运行JUnit CustomerCotollerTest后,即可查看打印的日志。以下就是从笔者的机器上启动的测试中获取的日志。可以看到,第一个请求来自account-service服务,被负载均衡到第一个实例, 延迟了200 毫秒。为方便查看,笔者在日志后面以双斜杠注释方式给它添加了标记(1),后面的标记格式相同,兹不赘述。

接下来,发送到9091端口上可用实例的每个请求因为需延迟2000毫秒,所以都会在一秒钟后因为超时而结束。在发送10次请求之后,首次由于故障而导致电路跳闸,详见日志标记(2)。

然后,在接下来的10秒内,每个请求都由- -个回退方法处理,该方法将返回缓存的数据,见日志标记(3)和(4)。

10秒之后,客户端尝试再次调用account-service 服务实例,因为它再次被负载均衡到延迟为200毫秒,所以它成功了,见日志标记(5)。

这种成功导致电路的闭合。但糟糕的是,接下来的account-service服务实例仍然响应缓慢(因为又要延迟2000毫秒),因而上述情况会再次发生,直到JUnit测试结束,见日志标记(6)和(7)。

以上就是关于Hystrix的断路器在SpringCloud中的工作方式的详细说明。

16:54:04+01:00 Found response delay setting for this request host:
{account-service:8091 200} // (1)
16:54:05+01:00 Found response delay setting for this request host:
{account- service: 90912000}
16:54:05+01:00 Found response delay setting for this request host:
{account- service:8091200}
16:54: 06+01:00 Found response delay setting for this request host:
{account-service:9091 2000}
16:54:06+01:00 Found response delay setting for this request host:
{account- service:8091200}
16:54:09+01:00 Found response delay setting for this request host:
{account-service:9091 2000} // (2)
16:54:10.137 Customer [id=1, name John Scott, type -NEW, accounts= [Account
[id=1, number 1234567890,balance=5000]]] // (3)
...
16:54:20.169 Customer [id=1, name=John Scott, type NEW, accounts= [Account
[id=1, number=1234567890, balance=50001]] // (4)
16:54:20+01:00 Found response delay setting for this request host:
{account-service:8091 200} // (5)
16:54:20+01:00 Found response delay setting for this request host:
{ account-service:90912000}
16:54:21+01:00 Found response delay setting for this request host:
( account-service:8091 200}
16:54:25+01:00 Found response delay setting for this request host:
{account-service:8091 200} // (6)
16:54:26.157 Customer [id=1, name John Scott, type-NEW, accounts= [Account
| [id=1, number -1234567890, balance=500011] // (7)