springcloud 总集:https://www.tapme.top/blog/detail/2019-02-28-11-33

本次用到全部代码见文章最下方。

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

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

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

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

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

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

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

  上一篇已经说过,这里不再赘述。

  • 断路器(circuit breaker)模式

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

  • 后备(fallback)模式

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

  • 舱壁(bulkhead)模式

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

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

三、spring cloud 中使用

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

1、代码修改

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/,可以发现调用失败时,会启用后备方法。

  1. 在 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 中的,方便统一管理。

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

本篇原创发布于:FleyX 的个人博客

springCloud学习3(Netflix Hystrix弹性客户端)的更多相关文章

  1. SpringCloud学习笔记(3)——Hystrix

    参考Spring Cloud官方文档第13.14.15章 13. Circuit Breaker: Hystrix Clients Netflix提供了一个叫Hystrix的类库,它实现了断路器模式. ...

  2. springCloud学习-断路器(Hystrix)

    1.问题分析 在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用.为了保证其高 ...

  3. springCloud学习总览

      写完最后一篇特意去看了看第一篇是什么时候写的---2018/11/19,到现在三个月多一点,总的来说这三个月通过<Spring 微服务实战>这本书,算是对微服务进行了一次扫盲学习.   ...

  4. springcloud学习04- 断路器Spring Cloud Netflix Hystrix

    依赖上个博客:https://www.cnblogs.com/wang-liang-blogs/p/12072423.html 1.断路器存在的原因 引用博客 https://blog.csdn.ne ...

  5. SpringCloud学习笔记(14)----Spring Cloud Netflix之Hystrix对Feign的支持

    1. Hystrix对Feign的支持 添加Feign中IUserBiz的实现类HystrixFallBack: package com.wangx.cloud.springcloud02consum ...

  6. SpringCloud学习笔记(12)----Spring Cloud Netflix之Hystrix断路器的流程和原理

    工作流程(参考:https://github.com/Netflix/Hystrix/wiki/How-it-Works) 1. 创建一个HystrixCommand或HystrixObservabl ...

  7. SpringCloud学习笔记(4):Hystrix容错机制

    简介 在微服务架构中,微服务之间的依赖关系错综复杂,难免的某些服务会出现故障,导致服务调用方出现远程调度的线程阻塞.在高负载的场景下,如果不做任何处理,可能会引起级联故障,导致服务调用方的资源耗尽甚至 ...

  8. SpringCloud学习系列之三----- 断路器(Hystrix)和断路器监控(Dashboard)

    前言 本篇主要介绍的是SpringCloud中的断路器(Hystrix)和断路器指标看板(Dashboard)的相关使用知识. SpringCloud Hystrix Hystrix 介绍 Netfl ...

  9. SpringCloud学习笔记(七):Hystrix断路器

    概述 什么时候需要断路器?熔断? 举个简单的例子:小明喜欢小美,可是小美没有电话,小美给了小明家里的座机,小明打给座机,这个时候小美的妈妈接到了,小明怕妈妈知道自己喜欢小美,就跟小美妈妈说让小美哥接电 ...

随机推荐

  1. linux 实时监控网速脚本(转)

    #!/bin/bash ethn=$ while true do RX_pre=$(cat /proc/net/dev | grep $ethn | sed 's/:/ /g' | awk '{pri ...

  2. Keras split train test set when using ImageDataGenerator

    Keras split train test set when using ImageDataGenerator I have a single directory which contains su ...

  3. cannot connect to daemon at tcp:5037: cannot connect to 127.0.0.1:5037: 由于目标计算机积极拒绝,无法连接。 (10061)

    原因是:5037端口被占用 解决方法:1.查找5037被谁占用   netstat -ano | findstr "3037" 2.查看对应的是哪个任务  tasklist | f ...

  4. 爬虫中采集动态HTML介绍

    JavaScript JavaScript 是网络上最常用也是支持者最多的客户端脚本语言.它可以收集 用户的跟踪数据,不需要重载页面直接提交表单,在页面嵌入多媒体文件,甚至运行网页游戏. 我们可以在网 ...

  5. Ubuntu系统配置双网卡ssh放外网

    (下面的IP,最后一位都用X代替) 先看看网卡设备的名字,下面要用(一般就是前两个):ifconfigsudo lshw -C network| grep -e  'logical name' 设置服 ...

  6. .net framework msbuild环境搭建 (不装vs)

    1. 安装 .net framework sdk 小于等于 .net framework 4.5 版本的 .net framework sdk 通过 windows sdk 工具安装 从下面额链接找到 ...

  7. 如何利用maven将本地jar包引入到工程之中

    1.在个人开发模式下,可以将jar包放入项目的lib目录中,在pom.xml中做如下配置: <dependencies> <dependency> <groupId> ...

  8. Intellij IDEA设置类注释和方法注释

    背景:工欲善其事必先利其器,如果不能把工具用熟练了, 感觉很是别扭. 参考:IntelliJ IDEA设置类注释和方法注释 IntelliJ IDEA 中创建类的时候,可以直接自动给类加注释的设置,以 ...

  9. django数据库配置,即数据库分库分表

    一 Django的数据库配置 (一)修改settings.py文件关于数据库的配置: Django默认使用sqlite:   DATABASES = { 'default': { 'ENGINE': ...

  10. 使用Mysql中的concat函数或正则匹配来快速批量生成用于执行的sql语句

    背景介绍 今天需要给一张表里面补数据,需要按照行的维度进行update,如果是个别数据那么直接写update语句就可以了,但是场景要求的是将整表的数据进行update,要实现这个需求就不能只靠蛮力了, ...