我们要解决什么问题?
在分布式环境中,我们的服务可能会通过远程调用与其他服务或应用程序进行交互,这些外部服务可能会因为停机、资源不可用等多种原因而随时不可用。这种由于外部应用程序故障而导致的不可用也会导致影响我们完美运行的应用程序。
例如,我们知道在微服务架构中,许多服务彼此交互,因此,如果我们有一个服务 A,其唯一目的是通过休息调用处理数据并将数据传递给服务 B,那么理想情况下,将假设服务 B 将始终可供服务 A 发送数据并返回正确的响应。但是,我们并不生活在一个理想的世界,不是吗?很有可能服务 B 由于 pod 故障、资源问题等原因而宕机,当轮到服务 A 发送数据时,它发现它尝试发送数据的端点不再可用。那么,它对该数据以及之后的数据有什么作用呢?
那么,服务 B 不可用并不是服务 A 的错,对吧?它正在尽力而为,但它也不知道现在该怎么办。解决方案是让服务A具有容错能力。我们希望服务能够优雅地处理故障并对故障采取行动,这不会影响服务本身的工作。无论服务 B 发生什么情况,服务 A 都不应该受到影响,并且它应该继续处理数据以传递给服务 B。我们可以通过使用断路器模式来实现这一点。
断路器模式
断路器是一种模式,可以防止我们的服务重复重试失败的操作,并允许它连续工作,无论故障是什么以及修复需要多长时间,从而节省我们的 CPU 资源。此模式还可以帮助我们监视故障是否已解决,如果已解决,它将再次继续通过原始操作进行调用。
什么是 Netflix-hystrix?
简而言之,断路器是一种模式,Netflix-hystrix 是该模式的实现。该库旨在通过添加延迟容忍和容错逻辑来控制分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点、阻止服务之间的级联故障并提供后备选项来实现这一点——所有这些都提高了系统的整体弹性。
Netflix-hystrix 的特点:
- 提供保护并控制依赖项造成的延迟和故障
- 阻止复杂分布式系统中的级联故障
- 回退机制
- 实现近乎实时的监控、警报和操作控制
让我们自己实现这个,以便更好地理解和使用 netflix-hystrix 与 Java Springboot 应用程序。
在这里,我们创建了一个示例 Hystrix Java 服务,该服务执行以下操作:
- 公开提供有关服务的信息的 GET 端点。
- 该服务在内部调用另一个端点来检索此信息。这又是一次休息电话。
- 当我们到达服务端点时,控制器将执行此方法。
public List<ServiceInformation> showServiceInformation() {
List<ServiceInformation> information = restTemplate.exchange(
"http://localhost:8081", HttpMethod.GET, null, new
ParameterizedTypeReference<List<ServiceInformation>>(){}).getBody();
for(ServiceInformation serviceInformation : information) {
logger.trace(serviceInformation.getServiceName());
logger.trace(serviceInformation.getMessage());
}
return information;
}
现在,我们可以看到该方法对另一个服务进行了休息调用,因此只有外部服务返回时我们才能接收信息List<ServiceInformation>
但是,如果此休息端点不可用,我们将不会收到任何数据。这是我们可以使用 Netflix-hystrix 实现断路器模式的地方。
该库为我们提供了回退方法机制,这是在调用外部服务失败时将被调用的方法。后备方法的逻辑取决于您的用例;在这里,我们只是为了演示目的而记录服务失败并且应用程序转移到后备方法。
@HystrixCommand(fallbackMethod = "defaultStatus")
public List<ServiceInformation> showServiceInformation() {
List<ServiceInformation> information = restTemplate.exchange(
"http://localhost:8081", HttpMethod.GET, null, new
ParameterizedTypeReference<List<ServiceInformation>>(){}).getBody();
for(ServiceInformation serviceInformation : information) {
logger.trace(serviceInformation.getServiceName());
logger.trace(serviceInformation.getMessage());
}
return information;
}
public List<ServiceInformation> defaultStatus() {
logger.error("circuit-breaker-proxy is down, running fallback method");
return Collections.emptyList();
}
@HystrixCommand()
使我们能够提供后备方法。回退方法的名称在此注解的参数中传递。
这里,默认状态是fallback方法,并且该方法的名称作为注解中的参数传递@HystrixCommand
,如果外部服务调用失败,则将执行fallback方法。我们还可以为后备方法提供后备。