了解spring中的CloudNetflix Hystrix弹性客户端

一、为什么要有客户端弹性模式

所有的系统都会遇到故障,分布式系统单点故障概率更高。如何构建应用程序来应对故障,是每个软件开发人员工作的关键部分。但是通常在构建系统时,大多数工程师只考虑到基础设施或关键服务彻底发生故障,使用诸如集群关键服务器、服务间的负载均衡以及异地部署等技术。尽管这些方法考虑到组件系统的彻底故障,但他们之解决了构建弹性系统的一小部分问题。当服务崩溃时,很容易检测到该服务以及失效,因此应用程序可以饶过它。然而,当服务运行缓慢时,检测到这个服务性能越发低下并绕过它是非常困难的,因为以下几个原因:

  • 服务的降级可以是以间歇性的故障开始,并形成不可逆转的势头————可能开始只是一小部分服务调用变慢,直到突然间应用程序容器耗尽了线程(所有线程都在等待调用完成)并彻底崩溃。
  • 应用程序通常的设计是处理远程资源的彻底故障,而不是部分降级————通常,只要服务没有完全死掉,应用程序将继续调用这个服务,直到资源耗尽崩溃。

性能较差的远程服务会导致很大的潜在问题,它们不仅难以检测,还会触发连锁反应,从而影响整个应用程序生态系统。如果没有适当的保护措施,一个性能不佳的服务可以迅速拖垮整个应用程序。基于云、基于微服务的应用程序特别容易受到这些类型的终端影响,因为这些应用由大量细粒度的分布式服务组成,这些服务在完成用户的事务时涉及不同的基础设施。

二、什么是客户端弹性模式

客户端弹性模式是在远程服务发生错误或表现不佳时保护远程资源(另一个微服务调用或者数据库查询)免于崩溃。这些模式的目标是为了能让客户端“快速失败”,不消耗诸如数据库连接、线程池之类的资源,还可以避免远程服务的问题向客户端的消费者进行传播,引发“雪崩”效应。spring cloud 主要使用的有四种客户端弹性模式:

客户端负载均衡(client load balance)模式

断路器(circuit breaker)模式

本模式模仿的是电路中的断路器。有了软件断路器,当远程服务被调用时,断路器将监视这个调用,如果调用时间太长,断路器将介入并中断调用。此外,如果对某个远程资源的调用失败次数达到某个阈值,将会采取快速失败策略,阻止将来调用失败的远程资源。

后备(fallback)模式

当远程调用失败时,将执行替代代码路径,并尝试通过其他方式来处理操作,而不是产生一个异常。也就是为远程操作提供一个应急措施,而不是简单的抛出异常。

舱壁(bulkhead)模式

舱壁模式是建立在造船的基础概念上。我们都知道一艘船会被划分为多个水密舱(舱壁),因而即使少数几个部位被击穿漏水,整艘船并不会被淹没。将这个概念带入到远程调用中,如果所有调用都使用的是同一个线程池来处理,那么很有可能一个缓慢的远程调用会拖垮整个应用程序。在舱壁模式中可以隔离每个远程资源,并分配各自的线程池,使之互不影响。

下图展示了这些模式是如何运用到微服务中的:

三、spring cloud 中使用

使用 Netflix 的 Hystrix 库来实现上述弹性模式。继续使用上一节的项目,给 licensingservice 服务实现弹性模式。

1、代码修改

依赖引入

首先修改 POM 文件,添加下面两个依赖:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<!--本依赖不是必须的,spring-cloud-starter-hystrix已经带了,但是在Camden.SR5发行版本中使用了1.5.6,这个版本有一个不一致的地方,在没有后备的情况下会抛出java.lang.reflect.UndeclaredThrowableException而不是com.netflix.hystrix.exception.HystrixRuntimeException,
在后续版本中修复了这个问题-->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.9</version>
</dependency>

然后在启动类上加入@EnableCircuitBreaker启用 Hystrix。

2、实现断路器

首先修改 organizationservice 项目中的 OrganizationController,模拟延迟,每隔两次让线程 sleep 2 秒

@RestController
public class OrganizationController {
private static int count=1;
@GetMapping(value = "/organization/{orgId}")
public Object getOrganizationInfo(@PathVariable("orgId") String orgId) throws Exception{
if(count%2==0){
TimeUnit.SECONDS.sleep(2);
}
count++;
Map<String, String> data = new HashMap<>(2);
data.put("id", orgId);
data.put("name", orgId + "公司");
return data;
}
}

只需在方法上添加@HystrixCommand,即可实现超时短路。如果 Spring 扫描到该注解注释的类,它将动态生成一个代理,来包装这个方法,并通过专门用于处理远程调用的线程池来管理对该方法的所有调用。

修改 licensingservice 服务中的 OrganizationByRibbonService,OrganizationFeignClient,给其中的方法加上@HystrixCommand的注解。

然后再访问接口localhost:10011/licensingByRibbon/11313,localhost:10011/licensingByFeign/11313。多次访问可发现抛出错误com.netflix.hystrix.exception.HystrixRuntimeException,断路器生效,默认情况下操时时间为 1s。

{
"timestamp": 1543823192424,
"status": 500,
"error": "Internal Server Error",
"exception": "com.netflix.hystrix.exception.HystrixRuntimeException",
"message": "OrganizationFeignClient#getOrganization(String) timed-out and no fallback available.",
"path": "/licensingByFeign/11313/"
}

可通过设置注解参数来修改操时时间。设置超时时间大于 2s 后便不会报操时错误。(不知道为什么在 Feign 中设置失败,ribbon 中正常。)。一般都是将配置写在配置文件中。

@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "20000")
})

3、后备处理

由于远程资源的消费者和资源本身之间存在存在一个"中间人",因此开发人员能够拦截服务故障,并选择替代方案。在 Hystrix 中进行后备处理,非常容易实现。

1.在ribbon 中的实现

只需在@HystrixCommand注解中加入属性 fallbackMethod="methodName",那么在执行失败时,便会执行后备方法。注意防备方法必须和被保护方法在同一个类中,并且方法签名必须相同。修改 licensingservice 中 service 包下的 OrganizationByRibbonService 类,改为如下:

@Component
public class OrganizationByRibbonService {
private RestTemplate restTemplate;
@Autowired
public OrganizationByRibbonService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
},fallbackMethod = "getOrganizationWithRibbonBackup")
public Organization getOrganizationWithRibbon(String id) throws Exception {
ResponseEntity<Organization> responseEntity = restTemplate.exchange("http://organizationservice/organization/{id}",
HttpMethod.GET, null, Organization.class, id);
return responseEntity.getBody();
}
public Organization getOrganizationWithRibbonBackup(String id)throws Exception{
Organization organization = new Organization();
organization.setId("0");
organization.setName("组织服务调用失败");
return organization;
}
}

启动应用,多次访问localhost:10011/licensingByRibbon/11313/,可以发现调用失败时,会启用后备方法。

2.在feign 中实现

在 feign 中实现后备模式,需要编写一个 feign 接口的实现类,然后在 feign 接口中指定该类。以 licensingservice 为例。首先在 client 包中添加一个 OrganizationFeignClientImpl 类,代码如下:

@Component
public class OrganizationFeignClientImpl implements OrganizationFeignClient{
@Override
public Organization getOrganization(String orgId) {
Organization organization=new Organization();
organization.setId("0");
organization.setName("后备模式返回的数据");
return organization;
}
}

然后修改 OrganizationFeignClient 接口的注解,将@FeignClient("organizationservice")改为@FeignClient(name="organizationservice",fallback = OrganizationFeignClientImpl.class。

重启项目,多次访问localhost:10011/licensingByFeign/11313/,可发现后备服务起作用了。

在确认是否要启用后备服务时,要注意以下两点:

  • 后备是一种在资源操时或失败时提供行动方案的机制。如果只是用后备来捕获操时异常然后只做日志记录,那只需要 try..catch 即可,捕获 HystrixRuntimeException 异常。
  • 注意后备方法所执行的操作。如果在后备服务中调用另一个分布式服务,需要注意用@HystrixCommand 方法注解包装后备方法。

4、实现舱壁模式

在基于微服务的应用程序中,通常需要调用多个微服务来完成特定的任务,在不适用舱壁的模式下,这些调用默认是使用同一批线程来执行调用的,而这些线程是为了处理整个 Java 容器的请求而预留的。因此在存在大量请求的情况下,一个服务出现性能问题会导致 Java 容器内的所有线程被占用,同时阻塞新请求,最终容器彻底崩溃。

Hystrix 使用线程池来委派所有对远程服务的调用,默认情况下这个线程池有 10 个工作线程。但是这样很容易出现一个运行缓慢的服务占用全部的线程,所有 hystrix 提供了一种一种易于使用的机制,在不同的远程资源调用间创建‘舱壁',将不同服务的调用隔离到不同的线程池中,使之互不影响。

要实现隔离的线程池,只需要在@HystrixCommand上加入线程池的注解,这里以 ribbon 为例(Feign 类似)。修改 licensingservice 中 service 包下的 OrganizaitonByRibbonService 类,将getOrganizationWithRibbon方法的注解改为如下:

@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
}, fallbackMethod = "getOrganizationWithRibbonBackup",
threadPoolKey = "licenseByOrgThreadPool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "10")
})

如果将maxQueueSize属性值设为-1,将使用SynchronousQueue保存所有的传入请求,同步队列会强制要求正在处理中的请求数量永远不能超过线程池的大小。设为大于 1 的值将使用LinkedBlockingQueue。

注意:示例代码中都是硬编码属性值到 Hystrix 注解中的。在实际应用环境中,一般都是将配置项配置在 Spring Cloud Config 中的,方便统一管理。

本次用到全部代码:点击跳转

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Cloud Netflix架构浅析(小结)

    最近接触微服务这块的东西,对这方面有了一些了解,拿出来和大家分享一下. 1. 微服务框架Spring Boot+Spring Cloud  Spring Cloud是基于Spring Boot的一整套实现微服务的框架,可以说,Spring Boot作为框架,Spring Cloud作为微服务,一起构成了一种不可忽视的.新生的框架体系.它提供了微服务开发所需的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集群状态管理等组件,方便易用.Spring Cloud

  • springcloud使用Hystrix进行微服务降级管理

    前言:目前我们的项目是微服务架构,基于dubbo框架,服务之间的调用是通过rpc调用的.刚开始没有任何问题,项目运行健康.良好.可是过了一段时间,线上总有人反应查询订单失败,等过了一段时间才能查到.这是怎么回事呢?打开后台的日志一看出现了一些RpcException和TimeOutException,原来是远程调用超时了,可能某个服务在请求的高发期访问数据库异常,IO阻塞,返回接口异常了.后来这个问题越来越频繁,如何解决这个棘手的问题呢? 一:Hystrix是什么? 1.1:基本解释 Hystr

  • Spring Cloud Hystrix 线程池队列配置(踩坑)

    背景: 有一次在生产环境,突然出现了很多笔还款单被挂起,后来排查原因,发现是内部系统调用时出现了Hystrix调用异常.在开发过程中,因为核心线程数设置的比较大,没有出现这种异常.放到了测试环境,偶尔有出现这种情况,后来在网上查找解决方案,网上的方案是调整maxQueueSize属性就好了,当时调整了一下,确实有所改善.可没想到在生产环境跑了一段时间后却又出现这种了情况,此时我第一想法就是去查看maxQueueSize属性,可是maxQueueSize属性是设置值了.当时就比较纳闷了,为什么ma

  • 详解Spring Cloud Netflix Zuul中的速率限制

    Spring Cloud Netflix Zuul是一个包含Netflix Zuul的 开源网关.它为Spring Boot应用程序添加了一些特定功能.不幸的是,开箱即用不提供速率限制. 除了Spring Cloud Netflix Zuul依赖项之外,我们还需要将Spring Cloud Zuul RateLimit 添加到我们的应用程序的pom.xml中: <dependency> <groupId>org.springframework.cloud</groupId&g

  • 详解spring-cloud与netflixEureka整合(注册中心)

    基础依赖 compile('org.springframework.boot:spring-boot-starter-actuator') compile('org.springframework.boot:spring-boot-starter-web') compile('org.springframework.cloud:spring-cloud-starter') eureka(单机) 服务端: 依赖 compile('org.springframework.cloud:spring-c

  • 了解spring中的CloudNetflix Hystrix弹性客户端

    一.为什么要有客户端弹性模式 所有的系统都会遇到故障,分布式系统单点故障概率更高.如何构建应用程序来应对故障,是每个软件开发人员工作的关键部分.但是通常在构建系统时,大多数工程师只考虑到基础设施或关键服务彻底发生故障,使用诸如集群关键服务器.服务间的负载均衡以及异地部署等技术.尽管这些方法考虑到组件系统的彻底故障,但他们之解决了构建弹性系统的一小部分问题.当服务崩溃时,很容易检测到该服务以及失效,因此应用程序可以饶过它.然而,当服务运行缓慢时,检测到这个服务性能越发低下并绕过它是非常困难的,因为

  • SpringCloud中的断路器(Hystrix)和断路器监控(Dashboard)

    前言 本篇主要介绍的是SpringCloud中的断路器(Hystrix)和断路器指标看板(Dashboard)的相关使用知识. SpringCloud Hystrix Hystrix 介绍 Netflix创建了一个名为Hystrix的库,它实现了断路器模式.主要的目的是为了解决服务雪崩效应的一个组件,是保护服务高可用的最后一道防线. 开发准备 开发环境 •JDK:1.8 •SpringBoot:2.1.1.RELEASE •SpringCloud:Finchley 注:不一定非要用上述的版本,可

  • 详解spring中使用solr的代码实现

    在介绍solr的使用方法之前,我们需要安装solr的服务端集群.基本上就是安装zookeeper,tomcat,jdk,solr,然后按照需要配置三者的配置文件即可.由于本人并没有具体操作过如何进行solr集群的搭建.所以关于如何搭建solr集群,读者可以去网上查看其它资料,有很多可以借鉴.这里只介绍搭建完solr集群之后,我们客户端是如何访问solr集群的. 之前介绍过,spring封装nosql和sql数据库的使用,都是通过xxxTemplate.solr也不例外. 我们需要引入solr的j

  • spring中ioc是什么

    IoC--Inversion of Control,控制反转 在Java开发中,IoC意味着将你设计好的类交给系统去控制,而不是在你的类内部控制.IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则. DI--Dependency Injection(依赖注入) 即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中. 依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活.

  • Spring 中jdbcTemplate 实现执行多条sql语句示例

    说一下Spring框架中使用jdbcTemplate实现多条sql语句的执行: 很多情况下我们需要处理一件事情的时候需要对多个表执行多个sql语句,比如淘宝下单时,我们确认付款时要对自己银行账户的表里减去订单所需的钱数,即需要更新银行账户的表,同时需要更新淘宝订单的表将订单状态改为"已付款",这就需要先后执行多个sql(仅仅用于表达执行多的SQL的举例说明,具体淘宝如何实现并不是很清楚~~~~~); 但如果这中间出现电脑断网断电等问题,仅将我们银行账户的钱扣掉了,订单状态并没有改,那我

  • 理解Spring中的依赖注入和控制反转

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解. IoC是什么 Ioc-InversionofControl,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内

  • 浅谈spring中scope作用域

    今天研究了一下scope的作用域.默认是单例模式,即scope="singleton".另外scope还有prototype.request.session.global session作用域.scope="prototype"多例.再配置bean的作用域时,它的头文件形式如下: 如何使用spring的作用域: <bean id="role" class="spring.chapter2.maryGame.Role" s

  • 浅谈Spring中Bean的作用域、生命周期

    本文主要探究的是关于Bean的作用域.生命周期的相关内容,具体如下. Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).prototype(原型).request.session和global session,5种作用域说明如下: 1.singleton:单例模式,Spring IoC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象.Singleton作用域是Spring中的缺省作用域,也可以显示的将Bean定义为

  • Spring中的REST分页的实现代码

    本文将介绍在REST API中实现分页的基础知识.我们将专注于使用Spring Boot和Spring Data 在Spring MVC中构建REST分页. 分页是一种处理大结果数据集的机制.在REST API中实现分页并没有什么不同,但需要一些额外的思考过程.为REST API提供流畅有效的分页可以增加用户体验并有助于构建高效,快速的REST API.我们使用Spring Boot作为示例. 1.资源与表示 在我们开始设计分页API之前,我们需要清楚地了解页面作为资源或资源的表示.我们需要记住

  • Spring中事务用法示例及实现原理详解

    前言 Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现. 关于事务,简单来说,就是为了保证数据完整性而存在的一种工具,其主要有四大特性:原子性,一致性,隔离性和持久性.对于Spring事务,其最终还是在数据库层面实现的,而Spring只是以一种比较优雅的方式对其进行封装支持.本文首先会通过一个简单的示例来讲解Spring事务是如何使用的,然后会讲解Spring是如何解析xml中的标签,并对事

随机推荐