浅析Spring Boot单体应用熔断技术的使用

壹、入围方案

Sentinel

  • github地址:https://sentinelguard.io/zh-cn/docs/introduction.html

  • 阿里出品,Spring Cloud Alibaba限流组件,目前持续更新中

  • 自带Dashboard,可以查看接口Qps等,并且可以动态修改各种规则

  • 流量控制,直接限流、冷启动、排队

  • 熔断降级,限制并发限制数和相应时间

  • 系统负载保护,提供系统级别防护,限制总体CPU等

  • 主要核心:资源,规则(流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则。),和指标

  • 文档非常清晰和详细,中文

  • 支持动态规则(推模式和拉模式)

Hystrix

  • github地址:https://github.com/Netflix/Hystrix/wiki

  • Netflix出品,Spring Cloud Netflix限流组件,已经停止新特性开发,只进行bug修复,最近更新为2018年,功能稳定

  • 有简单的dashboard页面

  • 以隔离和熔断为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制的初代熔断框架,异常统计基于滑动窗口

resilience4j

  • github地址:https://resilience4j.readme.io/docs

  • 是一款轻量、简单,并且文档非常清晰、丰富的熔断工具。是Hystrix替代品,实现思路和Hystrix一致,目前持续更新中

  • 需要自己对micrometer、prometheus以及Dropwizard metrics进行整合

  • CircuitBreaker 熔断

  • Bulkhead 隔离

  • RateLimiter QPS限制

  • Retry 重试

  • TimeLimiter 超时限制

  • Cache 缓存

自己实现(基于Guava)

  • 基于Guava的令牌桶,可以轻松实现对QPS进行限流

贰、技术对比

浅析Spring Boot单体应用熔断技术的使用_第1张图片

叁、应用改造

3.1、sentinel

3.1.1、引入依赖

1

2

3

4

5

<dependency>

  <groupId>com.alibaba.cloudgroupId>

  <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>

  <version>2.0.3.RELEASEversion>

dependency>

3.1.2、改造接口或者service层

@SentinelResource(value = "allInfos",fallback = "errorReturn")

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Inherited

public @interface SentinelResource {

  //资源名称

  String value() default "";

  

  //流量方向

  EntryType entryType() default EntryType.OUT;

  

  //资源类型

  int resourceType() default 0;

  

  //异常处理方法

  String blockHandler() default "";

  

  //异常处理类

  Class[] blockHandlerClass() default {};

  

  //熔断方法

  String fallback() default "";

  

  //默认熔断方法

  String defaultFallback() default "";

  

  //熔断类

  Class[] fallbackClass() default {};

  

  //统计异常

  Classextends Throwable>[] exceptionsToTrace() default {Throwable.class};

  

  //忽略异常

  Classextends Throwable>[] exceptionsToIgnore() default {};

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@RequestMapping("/get")

@ResponseBody

@SentinelResource(value = "allInfos",fallback = "errorReturn")

public JsonResult allInfos(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num){

    try {

      if (num % 2 == 0) {

        log.info("num % 2 == 0");

        throw new BaseException("something bad with 2", 400);

      }

      return JsonResult.ok();

    } catch (ProgramException e) {

      log.info("error");

      return JsonResult.error("error");

    }

  }

3.1.3、针对接口配置熔断方法或者限流方法

默认过滤拦截所有Controller接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

/**

   * 限流,参数需要和方法保持一致

   * @param request

   * @param response

   * @param num

   * @return

   * @throws BlockException

   */

  public JsonResult errorReturn(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num) throws BlockException {

    return JsonResult.error("error 限流" + num );

  }

  

  /**

   * 熔断,参数需要和方法保持一直,并且需要添加BlockException异常

   * @param request

   * @param response

   * @param num

   * @param b

   * @return

   * @throws BlockException

   */

  public JsonResult errorReturn(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num,BlockException b) throws BlockException {

    return JsonResult.error("error 熔断" + num );

  }

注意也可以不配置限流或者熔断方法。通过全局异常去捕获UndeclaredThrowableException或者BlockException避免大量的开发量

3.1.4、接入dashboard

1

2

3

4

5

6

spring:

 cloud:

  sentinel:

   transport:

    port: 8719

    dashboard: localhost:8080

浅析Spring Boot单体应用熔断技术的使用_第2张图片

3.1.5、规则持久化和动态更新

接入配置中心如:zookeeper等等,并对规则采用推模式

3.2、hystrix

3.2.1、引入依赖

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<dependency>

  <groupId>org.springframework.bootgroupId>

  <artifactId>spring-boot-starter-actuatorartifactId>

dependency>

<dependency>

  <groupId>org.springframework.cloudgroupId>

  <artifactId>spring-cloud-starter-netflix-hystrix-dashboardartifactId>

  <version>2.0.4.RELEASEversion>

dependency>

<dependency>

  <groupId>org.springframework.cloudgroupId>

  <artifactId>spring-cloud-starter-netflix-hystrixartifactId>

  <version>2.0.4.RELEASEversion>

dependency>

3.2.2、改造接口

@HystrixCommand(fallbackMethod = "timeOutError")

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

public @interface HystrixCommand {

  String groupKey() default "";

  

  String commandKey() default "";

  

  String threadPoolKey() default "";

  

  String fallbackMethod() default "";

  

  HystrixProperty[] commandProperties() default {};

  

  HystrixProperty[] threadPoolProperties() default {};

  

  Classextends Throwable>[] ignoreExceptions() default {};

  

  ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

  

  HystrixException[] raiseHystrixExceptions() default {};

  

  String defaultFallback() default "";

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

@RequestMapping("/get")

@ResponseBody

@HystrixCommand(fallbackMethod = "fallbackMethod")

public JsonResult allInfos(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num){

  try {

    if (num % 3 == 0) {

      log.info("num % 3 == 0");

      throw new BaseException("something bad whitch 3", 400);

    }

  

    return JsonResult.ok();

  } catch (ProgramException | InterruptedException exception) {

    log.info("error");

    return JsonResult.error("error");

  }

}

3.2.3、针对接口配置熔断方法

1

2

3

4

5

6

7

8

9

10

11

12

/**

 * 该方法是熔断回调方法,参数需要和接口保持一致

 * @param request

 * @param response

 * @param num

 * @return

 */

public JsonResult fallbackMethod(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num) {

  response.setStatus(500);

  log.info("发生了熔断!!");

  return JsonResult.error("熔断");

}

3.2.4、配置默认策略

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

hystrix:

 command:

  default:

   execution:

    isolation:

     strategy: THREAD

     thread:

      # 线程超时15秒,调用Fallback方法

      timeoutInMilliseconds: 15000

   metrics:

    rollingStats:

     timeInMilliseconds: 15000

   circuitBreaker:

    # 10秒内出现3个以上请求(已临近阀值),并且出错率在50%以上,开启断路器.断开服务,调用Fallback方法

    requestVolumeThreshold: 3

    sleepWindowInMilliseconds: 10000

3.2.5、接入监控

浅析Spring Boot单体应用熔断技术的使用_第3张图片

浅析Spring Boot单体应用熔断技术的使用_第4张图片

曲线:用来记录2分钟内流量的相对变化,我们可以通过它来观察到流量的上升和下降趋势。

集群监控需要用到注册中心

3.3、resilience4j

3.3.1、引入依赖

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

dependency>

  <groupId>org.springframework.bootgroupId>

  <artifactId>spring-boot-starter-webartifactId>

dependency>

  

<dependency>

  <groupId>org.springframework.bootgroupId>

  <artifactId>spring-boot-starter-testartifactId>

  <scope>testscope>

dependency>

  

<dependency>

  <groupId>io.github.resilience4jgroupId>

  <artifactId>resilience4j-spring-boot2artifactId>

  <version>1.6.1version>

dependency>

  

<dependency>

  <groupId>io.github.resilience4jgroupId>

  <artifactId>resilience4j-bulkheadartifactId>

  <version>1.6.1version>

dependency>

  

<dependency>

  <groupId>io.github.resilience4jgroupId>

  <artifactId>resilience4j-ratelimiterartifactId>

  <version>1.6.1version>

dependency>

  

<dependency>

  <groupId>io.github.resilience4jgroupId>

  <artifactId>resilience4j-timelimiterartifactId>

  <version>1.6.1version>

dependency>

可以按需要引入:bulkhead,ratelimiter,timelimiter等

3.3.2、改造接口

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

@RequestMapping("/get")

@ResponseBody

//@TimeLimiter(name = "BulkheadA",fallbackMethod = "fallbackMethod")

@CircuitBreaker(name = "BulkheadA",fallbackMethod = "fallbackMethod")

@Bulkhead(name = "BulkheadA",fallbackMethod = "fallbackMethod")

public JsonResult allInfos(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num){

  log.info("param----->" + num);

  try {

    //Thread.sleep(num);

  

    if (num % 2 == 0) {

      log.info("num % 2 == 0");

      throw new BaseException("something bad with 2", 400);

    }

  

    if (num % 3 == 0) {

      log.info("num % 3 == 0");

      throw new BaseException("something bad whitch 3", 400);

    }

  

    if (num % 5 == 0) {

      log.info("num % 5 == 0");

      throw new ProgramException("something bad whitch 5", 400);

    }

    if (num % 7 == 0) {

      log.info("num % 7 == 0");

      int res = 1 / 0;

    }

    return JsonResult.ok();

  } catch (BufferUnderflowException e) {

    log.info("error");

    return JsonResult.error("error");

  }

}

3.3.3、针对接口配置熔断方法

1

2

3

4

5

6

7

8

9

10

11

/**

 * 需要参数一致,并且加上相应异常

 * @param request

 * @param response

 * @param num

 * @param exception

 * @return

 */

public JsonResult fallbackMethod(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num, BulkheadFullException exception) {

  return JsonResult.error("error 熔断" + num );

}

3.3.4、配置规则

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

resilience4j.circuitbreaker:

  instances:

    backendA:

      registerHealthIndicator: true

      slidingWindowSize: 100

    backendB:

      registerHealthIndicator: true

      slidingWindowSize: 10

      permittedNumberOfCallsInHalfOpenState: 3

      slidingWindowType: TIME_BASED

      minimumNumberOfCalls: 20

      waitDurationInOpenState: 50s

      failureRateThreshold: 50

      eventConsumerBufferSize: 10

      recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate

  

resilience4j.retry:

  instances:

    backendA:

      maxRetryAttempts: 3

      waitDuration: 10s

      enableExponentialBackoff: true

      exponentialBackoffMultiplier: 2

      retryExceptions:

        - org.springframework.web.client.HttpServerErrorException

        - java.io.IOException

      ignoreExceptions:

        - io.github.robwin.exception.BusinessException

    backendB:

      maxRetryAttempts: 3

      waitDuration: 10s

      retryExceptions:

        - org.springframework.web.client.HttpServerErrorException

        - java.io.IOException

      ignoreExceptions:

        - io.github.robwin.exception.BusinessException

  

resilience4j.bulkhead:

  instances:

    backendA:

      maxConcurrentCalls: 10

    backendB:

      maxWaitDuration: 10ms

      maxConcurrentCalls: 20

  

resilience4j.thread-pool-bulkhead:

 instances:

  backendC:

   maxThreadPoolSize: 1

   coreThreadPoolSize: 1

   queueCapacity: 1

  

resilience4j.ratelimiter:

  instances:

    backendA:

      limitForPeriod: 10

      limitRefreshPeriod: 1s

      timeoutDuration: 0

      registerHealthIndicator: true

      eventConsumerBufferSize: 100

    backendB:

      limitForPeriod: 6

      limitRefreshPeriod: 500ms

      timeoutDuration: 3s

  

resilience4j.timelimiter:

  instances:

    backendA:

      timeoutDuration: 2s

      cancelRunningFuture: true

    backendB:

      timeoutDuration: 1s

      cancelRunningFuture: false

配置的规则可以被代码覆盖

3.3.5、配置监控

如grafana等

肆、关注点

  • 是否需要过滤部分异常

  • 是否需要全局默认规则

  • 可能需要引入其他中间件

  • k8s流量控制

  • 规则存储和动态修改

  • 接入改造代价

【后面的话】

个人建议的话,比较推荐sentinel,它提供了很多接口便于开发者自己拓展,同时我觉得他的规则动态更新也比较方便。最后是相关示例代码:单体应用示例代码

以上就是浅析Spring Boot单体应用熔断技术的使用的详细内容,更多关于Spring Boot单体应用熔断技术的资料请关注脚本之家其它相关文章!

分享不易,如果觉得分享对大家有帮助,老规矩,点赞、关注、留言支持哦!

你可能感兴趣的:(spring,boot,java,后端)