限流知识《高可用服务设计之二:Rate limiting 限流与降级

在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元间通过服务注册与订阅的方式互相依赖。由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会出现因等待出现故障的依赖方响应而形成任务积压,最终导致自身服务的瘫痪。

举个例子,在一个电商网站中,我们可能会将系统拆分成,用户、订单、库存、积分、评论等一系列的服务单元。用户创建一个订单的时候,在调用订单服务创建订单的时候,会向库存服务来请求出货(判断是否有足够库存来出货)。此时若库存服务因网络原因无法被访问到,导致创建订单服务的线程进入等待库存申请服务的响应,在漫长的等待之后用户会因为请求库存失败而得到创建订单失败的结果。如果在高并发情况之下,因这些等待线程在等待库存服务的响应而未能释放,使得后续到来的创建订单请求被阻塞,最终导致订单服务也不可用。

在微服务架构中,存在着那么多的服务单元,若一个单元出现故障,就会因依赖关系形成故障蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构就更加的不稳定。为了解决这样的问题,因此产生了断路器模式。

什么是断路器

断路器模式源于Martin Fowler的Circuit Breaker一文。“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。

熔断器设计中有三种状态,closed(关闭状态,流量可以正常进入)、open(即熔断状态,一旦错误达到阈值,熔断器将打开,拒绝所有流量)和half-open(半开状态,open状态持续一段时间后将自动进入该状态,重新接收流量,一旦请求失败,重新进入open状态,但如果成功数量达到阈值,将进入closed状态),见下图:

CLOSED关闭状态:允许流量通过。

OPEN打开状态:不允许流量通过,即处于降级状态,走降级逻辑。

HALF_OPEN半开状态:允许某些流量通过,并关注这些流量的结果,如果出现超时、异常等情况,将进入OPEN状态,如果成功,那么将进入CLOSED状态。

在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。

Netflix Hystrix

在Spring Cloud中使用了Hystrix 来实现断路器的功能。Hystrix是Netflix开源的微服务框架套件之一,该框架目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能。

下面我们来看看如何使用Hystrix。

Netfix Hystrix可以通过两种方式引入:

  1. 从服务组件角度分:ribbon和Feign;
  2. 从代码方式角度分:hystrix通过设置fallback和fallbackFactory属性触发请求容灾降级;

准备工作

在开始加入断路器之前,我们先拿之前构建两个微服务为基础进行下面的操作,主要使用下面几个工程:

在Ribbon为引入Hystirx之前

  • 依次启动eureka-server、compute-service、ribbon-consumer工程
  • 访问http://localhost:1111/可以看到注册中心的状态
  • 访问http://127.0.0.1:2250/add,调用ribbon-consumer的服务,该服务会去调用compute-service的服务,计算出10+20的值,页面显示30
  • 关闭compute-service服务,访问http://127.0.0.1:2250/add,我们获得了下面的报错信息

具体的操作及关键代码如下:

创建一个ribbon-consumer的项目,pom为:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <groupId>com.dxz</groupId>
  5. <artifactId>ribbon-consumer</artifactId>
  6. <version>0.0.1-SNAPSHOT</version>
  7. <parent>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-parent</artifactId>
  10. <version>1.3.5.RELEASE</version> <!--配合spring cloud版本 -->
  11. <relativePath /> <!-- lookup parent from repository -->
  12. </parent>
  13. <properties>
  14. <!--设置字符编码及java版本 -->
  15. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  16. <java.version>1.8</java.version>
  17. </properties>
  18. <dependencies>
  19. <!--增加eureka-server的依赖 -->
  20. <dependency>
  21. <groupId>org.springframework.cloud</groupId>
  22. <artifactId>spring-cloud-starter-eureka-server</artifactId>
  23. </dependency>
  24.  
  25. <!--用于测试的,本例可省略 -->
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-starter-test</artifactId>
  29. <scope>test</scope>
  30. </dependency>
  31. </dependencies>
  32.  
  33. <!--依赖管理,用于管理spring-cloud的依赖 -->
  34. <dependencyManagement>
  35. <dependencies>
  36. <dependency>
  37. <groupId>org.springframework.cloud</groupId>
  38. <artifactId>spring-cloud-starter-parent</artifactId>
  39. <version>Brixton.SR3</version> <!--官网为Angel.SR4版本,但是我使用的时候总是报错 -->
  40. <type>pom</type>
  41. <scope>import</scope>
  42. </dependency>
  43. </dependencies>
  44. </dependencyManagement>
  45.  
  46. <build>
  47. <plugins>
  48. <!--使用该插件打包 -->
  49. <plugin>
  50. <groupId>org.springframework.boot</groupId>
  51. <artifactId>spring-boot-maven-plugin</artifactId>
  52. </plugin>
  53. </plugins>
  54. </build>
  55. </project>

启动类:

  1. package com.dxz.ribbon;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
  6. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  7. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.web.client.RestTemplate;
  10.  
  11. @SpringBootApplication
  12. @EnableDiscoveryClient
  13. public class RibbonApplication {
  14.  
  15. @Bean
  16. @LoadBalanced
  17. RestTemplate restTemplate() {
  18. return new RestTemplate();
  19. }
  20.  
  21. public static void main(String[] args) {
  22. SpringApplication.run(RibbonApplication.class, args);
  23. }
  24.  
  25. }

SpringMvc类:

  1. package com.dxz.ribbon;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestMethod;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import org.springframework.web.client.RestTemplate;
  7.  
  8. @RestController
  9. public class ConsumerController {
  10.  
  11. @Autowired
  12. RestTemplate restTemplate;
  13.  
  14. @RequestMapping(value = "/add", method = RequestMethod.GET)
  15. public String add() {
  16. return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
  17. }
  18.  
  19. }

application配置:

  1. spring.application.name=ribbon-consumer
  2. server.port=2250
  3. eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

启动eureka-server,但不启动computer-service服务,启动ribbon-consumer服务并访问:

测试结果:

方式一:Ribbon中引入Hystrix

1、pom.xml中引入依赖hystrix依赖

  1. <!--增加hystrix的依赖 -->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-hystrix</artifactId>
  5. </dependency>

2、在eureka-ribbon的主类RibbonApplication中使用@EnableCircuitBreaker注解开启断路器功能:

  1. package com.dxz.ribbon;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
  6. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  7. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.web.client.RestTemplate;
  10.  
  11. @EnableCircuitBreaker
  12. @SpringBootApplication
  13. @EnableDiscoveryClient
  14. public class RibbonApplication {
  15.  
  16. @Bean
  17. @LoadBalanced
  18. RestTemplate restTemplate() {
  19. return new RestTemplate();
  20. }
  21.  
  22. public static void main(String[] args) {
  23. SpringApplication.run(RibbonApplication.class, args);
  24. }
  25.  
  26. }

3、改造原来的服务消费方式,新增ComputeService类,在使用ribbon消费服务的函数上增加@HystrixCommand注解来指定回调方法。

  1. package com.dxz.ribbon;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.web.client.RestTemplate;
  6.  
  7. import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
  8.  
  9. @Service
  10. public class ComputeService {
  11. @Autowired
  12. RestTemplate restTemplate;
  13.  
  14. @HystrixCommand(fallbackMethod = "addServiceFallback")
  15. public String addService() {
  16. return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20&sn=1", String.class).getBody();
  17. }
  18.  
  19. public String addServiceFallback() {
  20. return "error";
  21. }
  22. }

4、提供rest接口的Controller从使用RestTemplate调用改为调用ComputeService的addService方法:

  1. package com.dxz.ribbon;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestMethod;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.springframework.web.client.RestTemplate;
  8.  
  9. @RestController
  10. public class ConsumerController {
  11.  
  12. //@Autowired
  13. //RestTemplate restTemplate;
  14.  
  15. @Autowired
  16. private ComputeService computeService;
  17.  
  18. @RequestMapping(value = "/add", method = RequestMethod.GET)
  19. public String add() {
  20. //return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
  21. return computeService.addService();
  22. }
  23.  
  24. }

由于Hystrix默认超时时间为2000毫秒,所以这里采用了0至3000的随机数以让有一定概率发生超时来触发断路器。分别将服务提供方和消费方修改如下:

服务提供方,springcloud-computer:

  1. @RequestMapping(value = "/add", method = RequestMethod.GET)
  2. public Integer add(@RequestParam Integer a, @RequestParam Integer b, @RequestParam Integer sn) {
  3. ServiceInstance instance = client.getLocalServiceInstance();
  4. Integer r = a + b;
  5. try {
  6. int sleepTime = new Random().nextInt(3000);
  7. System.out.println("sleepTime=" + sleepTime);
  8. Thread.sleep(sleepTime);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r + ",sn="+sn +",time="+ LocalDateTime.now());
  13. return r;
  14. }

服务消费方,ribbon-consumer:

  1. package com.dxz.ribbon;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestMethod;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import org.springframework.web.client.RestTemplate;
  8.  
  9. @RestController
  10. public class ConsumerController {
  11.  
  12. //@Autowired
  13. //RestTemplate restTemplate;
  14.  
  15. @Autowired
  16. private ComputeService computeService;
  17.  
  18. @RequestMapping(value = "/add", method = RequestMethod.GET)
  19. public String add() {
  20. //return restTemplate.getForEntity("http://COMPUTE-SERVICE/add?a=10&b=20", String.class).getBody();
  21. Long start = System.currentTimeMillis();
  22. String temp = computeService.addService();
  23. Long end = System.currentTimeMillis();
  24. System.out.println("computeService.addService()="+temp + ",user time=" +(end-start) + "毫秒");
  25. return temp;
  26. }
  27.  
  28. }

结果:

随机出现熔断场景

方式二:Feign使用Hystrix

注意这里说的是“使用”,没有错,我们不需要在Feigh工程中引入Hystix,Feign中已经依赖了Hystrix,我们可以在未做任何改造前,尝试下面你的操作:

  • 依次启动eureka-server、compute-service、eureka-feign工程
  • 访问http://localhost:1111/可以看到注册中心的状态
  • 访问http://localhost:3333/add,调用eureka-feign的服务,该服务会去调用compute-service的服务,计算出10+20的值,页面显示30
  • 关闭compute-service服务,访问http://localhost:3333/add,我们获得了下面的报错信息
  1. Whitelabel Error Page
  2.  
  3. This application has no explicit mapping for /error, so you are seeing this as a fallback.
  4.  
  5. Sat Jun 25 22:10:05 CST 2016
  6. There was an unexpected error (type=Internal Server Error, status=500).
  7. add timed-out and no fallback available.

使用@FeignClient注解中的fallback属性指定回调类如果您够仔细,会发现与在ribbon中的报错是不同的,看到add timed-out and no fallback available这句,或许您已经猜到什么,看看我们的控制台,可以看到报错信息来自hystrix-core-1.5.2.jar,所以在这个工程中,我们要学习的就是如何使用Feign中集成的Hystrix。

  1. package com.dxz;
  2.  
  3. import org.springframework.cloud.netflix.feign.FeignClient;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestMethod;
  6. import org.springframework.web.bind.annotation.RequestParam;
  7.  
  8. //@FeignClient("compute-service")
  9. @FeignClient(value = "compute-service", fallback = ComputeClientHystrix.class)
  10. public interface ComputeClient {
  11.  
  12. @RequestMapping(method = RequestMethod.GET, value = "/add")
  13. Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b);
  14.  
  15. }
  • 创建回调类ComputeClientHystrix,实现被@FeignClient注解的接口,此时实现的方法就是对应@FeignClient接口中映射的fallback函数。
  1. package com.dxz;
  2.  
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.bind.annotation.RequestParam;
  5.  
  6. @Component
  7. public class ComputeClientHystrix implements ComputeClient {
  8. @Override
  9. public Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b) {
  10. return -9999;
  11. }
  12. }
  • 再用之前的方法验证一下,是否在compute-service服务不可用的情况下,页面返回了-9999。

三、fallbackFactory

fallbackFactory与fallback不同的是可以打印详细的错误信息

将原来的fallback替换成fallbackFactory

  1. @FeignClient(value = "message-api",fallbackFactory = MessageApiFailFactory.class)
  2. public interface MessageApiFeignClient {
  3.  
  4. @RequestMapping(value = "/example/hello",method = RequestMethod.GET)
  5. public String getMessage(@RequestParam("name") String name);
  6. }
  7. public interface MessageApiFeignFallBackFactoryClient extends MessageApiFeignClient{
  8. }
  9. @Component
  10. public class MessageApiFailFactory implements FallbackFactory<MessageApiFeignClient> {
  11.  
  12. public static final Logger logger = LoggerFactory.getLogger(MessageApiFailFactory.class);
  13. @Override
  14. public MessageApiFeignClient create(Throwable throwable) {
  15. logger.info("fallback; reason was: {}",throwable.getMessage());
  16. return new MessageApiFeignFallBackFactoryClient(){
  17.  
  18. @Override
  19. public String getMessage(String name) {
  20. return "错误原因:"+throwable.getMessage();
  21. }
  22. };
  23. }
  24. }

同样进行本地调用

  1. messageApiFeignClient.getMessage("zhangsan");

运行后服务调用方打印: 
错误原因:status 500 reading MessageApiFeignClient#getMessage(String); content: 
 {"timestamp":1508681203932,"status":500,"error":"Internal Server Error","exception":"java.lang.ArithmeticException","message":"/ by zero","path":"/example/hello"} 
另外配置文件下可以设置hystrix服务超时机制

  1. #开启hystrix请求超时机制 也可以设置成永久不超时
    hystrix.command.default.execution.timeout.enabled=false
  2. hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000

服务容错保护断路器Hystrix之一:入门示例介绍(springcloud引入Hystrix的两种方式)的更多相关文章

  1. WCF服务使用(IIS+Http)和(Winform宿主+Tcp)两种方式进行发布

    1.写在前面 刚接触WCF不久,有很多地方知其然不知其所以然.当我在[创建服务->发布服务->使用服务]这一过程出现过许多问题.如客户端找不到服务引用:客户端只在本机环境中才能访问服务,移 ...

  2. thinkphp 3.2.3 入门示例2(URL传参数的几种方式)

    原文:thinkphp中URL传参数的几种方式 在thinkphp中,url传参合asp.net中原理类似,下面就单个参数和多个参数传递方式进行一个简单讲解 1.传单个参数 单个参数这种比较简单,例如 ...

  3. 服务容错保护断路器Hystrix之三:断路器监控(Hystrix Dashboard)-单体监控

    turbine:英 [ˈtɜ:baɪn] 美 [ˈtɜ:rbaɪn] n.汽轮机;涡轮机;透平机 一.Hystrix Dashboard简介 在微服务架构中为了保证程序的可用性,防止程序出错导致网络阻 ...

  4. 服务容错保护断路器Hystrix之二:Hystrix工作流程解析

    一.总运行流程 当你发出请求后,hystrix是这么运行的 红圈 :Hystrix 命令执行失败,执行回退逻辑.也就是大家经常在文章中看到的“服务降级”. 绿圈 :四种情况会触发失败回退逻辑( fal ...

  5. 服务容错保护断路器Hystrix之五:配置

    接着<服务容错保护断路器Hystrix之二:Hystrix工作流程解析>中的<2.8.关于配置>再列举重要的配置如下 一.hystrix在生产中的建议 1.保持timeout的 ...

  6. 服务容错保护断路器Hystrix之七:做到自动降级

    从<高可用服务设计之二:Rate limiting 限流与降级>中的“自动降级”中,我们这边将系统遇到“危险”时采取的整套应急方案和措施统一称为降级或服务降级.想要帮助服务做到自动降级,需 ...

  7. SpringCloud系列-整合Hystrix的两种方式

    Hystrix [hɪst'rɪks],中文含义是豪猪,因其背上长满棘刺,从而拥有了自我保护的能力.本文所说的Hystrix是Netflix开源的一款容错框架,同样具有自我保护能力. 本文目录 一.H ...

  8. 基础知识:编程语言介绍、Python介绍、Python解释器安装、运行Python解释器的两种方式、变量、数据类型基本使用

    2018年3月19日 今日学习内容: 1.编程语言的介绍 2.Python介绍 3.安装Python解释器(多版本共存) 4.运行Python解释器程序两种方式.(交互式与命令行式)(♥♥♥♥♥) 5 ...

  9. 介绍编译的less的两种IDE工具

    介绍编译的less的两种IDE工具 现在css预编译越来越普及了,著名的有less.sass.stylus等等等等.功能上基本上都是大同小异.这些个玩意儿主要表达的意思就是:"像编程一样的编 ...

随机推荐

  1. (1)什么是web框架和http协议

    Django是一个web框架 web框架的本质:就是一个socket服务端 bs架构本质上就是cs架构(cs架构就是client和server):bs架构就是browser和server,本质上bro ...

  2. 高斯消元 o(n^3) 取摸和不取摸

    #include<bits/stdc++.h> using namespace std; ; int a[MAXN][MAXN];//增广矩阵 int x[MAXN];//解集 bool ...

  3. 【idea】清除类中无用的包

    快捷键 ctrl+alt+o 自动清除的配置方法 可以settings-general-auto import-java项,勾选optimize imports on the fly,在当前项目下会自 ...

  4. ie6,ie7下a标签无法点击(转载)

    前几天在做一个网站的引导页面,因为都是用图片,所以按钮需要用空a标签来做,发现a标签在IE6与IE7中点击无效中点击不了,其他浏览器都正常.一开始以为是z-index的问题,但不论z-index设置多 ...

  5. IAR intrinsic functions

    You can insert asm code example asm("NOP") into the c or c++ source code to get a good per ...

  6. Linux期末复习题

    版权声明: https://blog.csdn.net/u014483914/article/details/36622451 1.More和less命令的差别         More命令通经常使用 ...

  7. ios Programming:The Big Nerd Ranch Guid(6th Edition) (Joe Conway & AARON HILLEGASS 著)

    Introduction (已看) Prerequisites What Has Changed in the Sixth Edition? Our Teaching Philosophy How t ...

  8. 对HTML中的文字的修饰

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. bzoj 2739 最远点——分治处理决策单调性

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2739 分治处理决策单调性的思想就是先找到一个询问,枚举所有可能的转移找到它的决策点,那么这个 ...

  10. rtmp和http方式在播放flv方面的各自优势和劣势

    下面是查的一点资料,比较一下用fms的rtmp和web的http播放flv的差别: 1. 区别 用HTTP方式:先通过IIS 将FLV下载到本地缓存,然后再通过NetConnection的本地连接来播 ...