小言_互联网的博客

SpringCloud 服务限流与熔断

370人阅读  评论(0)

目录

学习SpringCloud指南 ☆ ☆ ☆ ☆ ☆

  小白学习SpringCloud 使用与Nacos

  小白学习SpringCloud 远程通信【OpenFeign】

  小白学习SpringCloud 配置中心【Nacos_Config】

  小白学习SpringCloud 网关【Gateway】

1. 限流

2. Gateway限流的实现

2.1 导入pom依赖

2.2 添加redis配置

2.3 添加限流配置

2.4 RequestRateLimiterConfig 配置类

2.5 整合yml文件参考

3. JMeter压力测试

4. 熔断

4.1 添加熔断配置 

4.2 整合yml文件参考

4.3 服务器降级响应处理


学习SpringCloud指南 ☆ ☆ ☆ ☆ ☆

  小白学习SpringCloud 使用与Nacos

  小白学习SpringCloud 远程通信【OpenFeign】

  小白学习SpringCloud 配置中心【Nacos_Config】

  小白学习SpringCloud 网关【Gateway】


问题分析 

     “一码通”一度瘫痪,西安电信遭质疑!崩溃原因到底是什么?_腾讯新闻 

      从技术角度分析一下,西安一码通为何反复崩溃? - 知乎  

 主要问题

  •  限流问题:市民在长时间无法刷出健康码的情况下,多次退出刷新重试,新的流量到达服务   器,导 致服务器压力变大、承受负载增加,说明“西安一码通”系统没有做好限流措施。 

  •  服务器问题:无论是企业和个人在租用服务器的时候都会受到峰值承受限制的,一旦超过服   务器的 承受能力,就会导致服务器瘫痪,应用程序暂停,网站无法访问。服务器是有峰值限   制的,不可能 承受无上限的并发能力。而造成服务器瘫痪的原因就是在同一段时间内,访问   人数多,造成高流量 的突进,超出了服务器的承受范围。

  •  架构问题:“西安一码通”功能影响“核酸检测”服务,说明模块间从界面到数据调用互相影响,   可能 不是微服务架构。 

  •  性能过载:典型的性能过载场景,不论内部根因是数据库瓶颈点,还是网络链接数瓶颈点等   等,外 因都是因为过载导致。 

  •  设计漏洞:没有考虑高流量高负载的情况,导致测试不充分;产品设计未考虑千万级的并发   访问, 交付前未进行同等级的压力测试。 

  •  压力测试:在市民长时间无法看到健康码的情况下,多次退出刷新重试,新的流量到达服务   器,导 致服务器压力变大、承受负载增加。说明压力测试不够。 

1. 限流

限流的目的是通过对并发访问/请求进行限速或者对一个时间窗口内的请求进行限速来保护系统,一旦达 到限制速率则可由拒绝服务,就是定向到错误页或友好的展示页,排队或等待。

限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击。在高并发的应用中,限流是 一个绕不开的话题。

 

令牌桶算法

 令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则   需要先从桶 里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

  •  QPS 每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。

提高带宽

 

2. Gateway限流的实现

    2.1 导入pom依赖


  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0 </modelVersion>
  5. <groupId>com.jmh </groupId>
  6. <artifactId>nacos_gateway </artifactId>
  7. <version>0.0.1-SNAPSHOT </version>
  8. <name>nacos_gateway </name>
  9. <description>Demo project for Spring Boot </description>
  10. <!--提供版本-->
  11. <properties>
  12. <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding>
  13. <maven.compiler.source>1.8 </maven.compiler.source>
  14. <maven.compiler.target>1.8 </maven.compiler.target>
  15. <!--<spring-boot.version>2.4.1</spring-boot.version>
  16. <spring-cloud.version>2020.0.0</spring-cloud.version>
  17. <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>-->
  18. <spring-boot.version>2.3.7.RELEASE </spring-boot.version>
  19. <spring-cloud.version>Hoxton.SR5 </spring-cloud.version>
  20. <spring-cloud-alibaba.version>2.1.1.RELEASE </spring-cloud-alibaba.version>
  21. </properties>
  22. <!--提供依赖-->
  23. <dependencies>
  24. <dependency>
  25. <groupId>org.springframework.boot </groupId>
  26. <artifactId>spring-boot-starter-test </artifactId>
  27. </dependency>
  28. <!--注册与发现-->
  29. <dependency>
  30. <groupId>com.alibaba.cloud </groupId>
  31. <artifactId>spring-cloud-starter-alibaba-nacos-discovery </artifactId>
  32. </dependency>
  33. <!--远程通信-->
  34. <dependency>
  35. <groupId>org.springframework.cloud </groupId>
  36. <artifactId>spring-cloud-starter-openfeign </artifactId>
  37. </dependency>
  38. <!--负载均衡-->
  39. <dependency>
  40. <groupId>org.springframework.cloud </groupId>
  41. <artifactId>spring-cloud-loadbalancer </artifactId>
  42. </dependency>
  43. <!--对象赋值依赖-->
  44. <dependency>
  45. <groupId>ma.glasnost.orika </groupId>
  46. <artifactId>orika-core </artifactId>
  47. <version>1.4.6 </version>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.projectlombok </groupId>
  51. <artifactId>lombok </artifactId>
  52. </dependency>
  53. <!--配置中心-->
  54. <!-- <dependency>
  55. <groupId>com.alibaba.cloud</groupId>
  56. <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  57. </dependency>
  58. <dependency>
  59. <groupId>org.springframework.cloud</groupId>
  60. <artifactId>spring-cloud-starter-bootstrap</artifactId>
  61. </dependency>-->
  62. <!--网关-->
  63. <dependency>
  64. <groupId>org.springframework.boot </groupId>
  65. <artifactId>spring-boot-starter-webflux </artifactId>
  66. </dependency>
  67. <dependency>
  68. <groupId>org.springframework.cloud </groupId>
  69. <artifactId>spring-cloud-starter-gateway </artifactId>
  70. </dependency>
  71. <dependency>
  72. <groupId>org.springframework.boot </groupId>
  73. <artifactId>spring-boot-starter-actuator </artifactId>
  74. </dependency>
  75. <!--<dependency>
  76. <groupId>org.springframework.cloud</groupId>
  77. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  78. <version>2.2.9.RELEASE</version>
  79. </dependency>-->
  80. <dependency>
  81. <groupId>io.projectreactor </groupId>
  82. <artifactId>reactor-test </artifactId>
  83. <scope>test </scope>
  84. </dependency>
  85. <dependency>
  86. <groupId>com.alibaba </groupId>
  87. <artifactId>fastjson </artifactId>
  88. <version>1.2.35 </version>
  89. </dependency>
  90. <!--限流-->
  91. <dependency>
  92. <groupId>org.springframework.boot </groupId>
  93. <artifactId>spring-boot-starter-data-redis-reactive </artifactId>
  94. </dependency>
  95. <!--熔断-->
  96. <dependency>
  97. <groupId>org.springframework.cloud </groupId>
  98. <artifactId>spring-cloud-starter-netflix-hystrix </artifactId>
  99. </dependency>
  100. </dependencies>
  101. <!--提供依赖版本-->
  102. <dependencyManagement>
  103. <dependencies>
  104. <dependency>
  105. <groupId>org.springframework.boot </groupId>
  106. <artifactId>spring-boot-dependencies </artifactId>
  107. <version>${spring-boot.version} </version>
  108. <type>pom </type>
  109. <scope>import </scope>
  110. </dependency>
  111. <dependency>
  112. <groupId>org.springframework.cloud </groupId>
  113. <artifactId>spring-cloud-dependencies </artifactId>
  114. <version>${spring-cloud.version} </version>
  115. <type>pom </type>
  116. <scope>import </scope>
  117. </dependency>
  118. <dependency>
  119. <groupId>com.alibaba.cloud </groupId>
  120. <artifactId>spring-cloud-alibaba-dependencies </artifactId>
  121. <version>${spring-cloud-alibaba.version} </version>
  122. <type>pom </type>
  123. <scope>import </scope>
  124. </dependency>
  125. </dependencies>
  126. </dependencyManagement>
  127. <build>
  128. <plugins>
  129. <plugin>
  130. <groupId>org.springframework.boot </groupId>
  131. <artifactId>spring-boot-maven-plugin </artifactId>
  132. </plugin>
  133. </plugins>
  134. </build>
  135. </project>
  • Spring Cloud Gateway官方提供了RequestRateLimiterGatewayFilterFactory类,使用redis和lua脚本 来实现令牌桶的方式。
  • Gateway通过内置的RequestRateLimiter过滤器实现限流,使用令牌桶算法,借助Redis保存中间数 据。用户可通过自定义KeyResolver设置限流维度。
  • 对请求的目标URL进行限流
  • 对来源IP进行限流
  • 特定用户进行限流

   2.2 添加redis配置


  
  1. redis:
  2. host: 127.0.0.1
  3. port: 6379
  4. # password: root123
  5. database: 0
  • 如果redis连接失败,限流功能将不能开启。因为没有redis作为容器来保存令牌,限流功能自  然就失效 了。
  • 可以将redis的配置信息保存到nacos中,通过添加nacos配置中心客户端的方式进行读取

   2.3 添加限流配置


  
  1. filters:
  2. - name: RequestRateLimiter
  3. args:
  4. #用于限流的键的解析器的 Bean 对象的名字,使用 SpEL表达式根据#{@beanName}获取Bean对象
  5. key-resolver: '#{@ipAddrKeyResolver}'
  6. #令牌桶填充速率,允许用户每秒处理多少个请求
  7. redis-rate-limiter.replenishRate: 10
  8. #令牌桶总容量,允许在一秒钟内完成的最大请求数
  9. redis-rate-limiter.burstCapacity: 20

  2.4 RequestRateLimiterConfig 配置类


  
  1. package com.jmh.nacos_gateway;
  2. import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.context.annotation.Primary;
  6. import reactor.core.publisher.Mono;
  7. /**
  8. * 请求限流配置
  9. */
  10. @SuppressWarnings("all")
  11. @Configuration
  12. public class RequestRateLimiterConfig {
  13. /**
  14. * 按IP来限流
  15. */
  16. @Bean
  17. @Primary
  18. public KeyResolver ipAddrKeyResolver () {
  19. return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
  20. }
  21. /**
  22. * 按用户限流
  23. */
  24. @Bean
  25. KeyResolver userKeyResolver () {
  26. return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst( "user"));
  27. }
  28. /**
  29. * 按URL限流,即以每秒内请求数按URL分组统计,超出限流的url请求都将返回429状态
  30. *
  31. * @return
  32. */
  33. @Bean
  34. KeyResolver apiKeyResolver () {
  35. return exchange -> Mono.just(exchange.getRequest().getPath().toString());
  36. }
  37. }

  2.5 整合yml文件参考


  
  1. server:
  2. port: 8084
  3. spring:
  4. application:
  5. name: nacos-gateway
  6. redis:
  7. host: 127.0.0.1
  8. port: 6379
  9. password: 1234
  10. database: 0
  11. cloud:
  12. nacos:
  13. discovery:
  14. server-addr: 127.0.0.1:8848
  15. gateway:
  16. discovery:
  17. locator:
  18. #是否与服务发现组件进行结合,通过service-id(必须设置成大写)转发到具体的服务实例。默认false
  19. #为true代表开启基于服务发现的路由规则。
  20. enabled: false
  21. #配置之后访问时service-id无需大写
  22. lower-case-service-id: true
  23. routes:
  24. # 路由标识(id:标识,具有唯一性)
  25. - id: user-consumer-api
  26. #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
  27. uri: lb://nacos-consumer
  28. #优先级,越小越优先
  29. #order: 999
  30. #路由条件(predicates:断言)
  31. predicates:
  32. # 路径匹配,
  33. - Path=/aa/**
  34. filters:
  35. #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
  36. #前缀过滤,请求地址:http://localhost:8084/usr/hello
  37. #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
  38. #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
  39. - StripPrefix=1
  40. #限流
  41. - name: RequestRateLimiter
  42. args:
  43. #用于限流的键的解析器的 Bean 对象的名字,使用 SpEL表达式根据#{@beanName}获取Bean对象
  44. key-resolver: '#{@ipAddrKeyResolver}'
  45. #令牌桶填充速率,允许用户每秒处理多少个请求
  46. redis-rate-limiter.replenishRate: 10
  47. #令牌桶总容量,允许在一秒钟内完成的最大请求数
  48. redis-rate-limiter.burstCapacity: 20
  49. # # 路由标识(id:标识,具有唯一性)
  50. # - id: user-provider-api
  51. # #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
  52. # uri: lb://nacos-provider
  53. # #优先级,越小越优先
  54. # #order: 999
  55. # #路由条件(predicates:断言)
  56. # predicates:
  57. # # 路径匹配,
  58. # - Path=/bb/**
  59. # filters:
  60. # #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
  61. # #前缀过滤,请求地址:http://localhost:8084/usr/hello
  62. # #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
  63. # #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
  64. # - StripPrefix=1
  65. #自定义动态路由配置
  66. gateway:
  67. nacos:
  68. server-addr: ${spring.cloud.nacos.discovery.server-addr}
  69. # namespace: xxx-xx-xx-xx
  70. data-id: gateway.json
  71. group: DEFAULT_GROUP

3. JMeter压力测试

压力测试是每一个Web应用程序上线之前都需要做的一个测试,他可以帮助我们发现系统中 的瓶颈问 题,减少发布到生产环境后出问题的几率 预估系统的承载能力,使我们能根据其做出一些应对措施。所以压力测试是一个非常重要的步骤

4. 熔断

在分布式系统中,网关作为流量的入口,大量请求进入网关,向后端远程系统或服务发起调用, 后端服务不可避免的会产生调用失败(超时或者异常),失败时不能让请求堆积在网关上,需要快速失 败并返回回去, 这就需要在网关上做熔断、降级操作。 

   4.1 添加熔断配置 


  
  1. filters:
  2. - name: Hystrix
  3. args:
  4. name: fallback
  5. fallbackUri: forward:/fallback

  
  1. hystrix:
  2. command:
  3. default:
  4. execution:
  5. isolation:
  6. thread:
  7. timeoutInMilliseconds: 300

   4.2 整合yml文件参考


  
  1. server:
  2. port: 8084
  3. spring:
  4. application:
  5. name: nacos-gateway
  6. redis:
  7. host: 127.0.0.1
  8. port: 6379
  9. password: 1234
  10. database: 0
  11. cloud:
  12. nacos:
  13. discovery:
  14. server-addr: 127.0.0.1:8848
  15. gateway:
  16. discovery:
  17. locator:
  18. #是否与服务发现组件进行结合,通过service-id(必须设置成大写)转发到具体的服务实例。默认false
  19. #为true代表开启基于服务发现的路由规则。
  20. enabled: false
  21. #配置之后访问时service-id无需大写
  22. lower-case-service-id: true
  23. routes:
  24. # 路由标识(id:标识,具有唯一性)
  25. - id: user-consumer-api
  26. #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
  27. uri: lb://nacos-consumer
  28. #优先级,越小越优先
  29. #order: 999
  30. #路由条件(predicates:断言)
  31. predicates:
  32. # 路径匹配,
  33. - Path=/aa/**
  34. filters:
  35. #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
  36. #前缀过滤,请求地址:http://localhost:8084/usr/hello
  37. #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
  38. #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
  39. - StripPrefix=1
  40. #限流
  41. - name: RequestRateLimiter
  42. args:
  43. #用于限流的键的解析器的 Bean 对象的名字,使用 SpEL表达式根据#{@beanName}获取Bean对象
  44. key-resolver: '#{@ipAddrKeyResolver}'
  45. #令牌桶填充速率,允许用户每秒处理多少个请求
  46. redis-rate-limiter.replenishRate: 10
  47. #令牌桶总容量,允许在一秒钟内完成的最大请求数
  48. redis-rate-limiter.burstCapacity: 20
  49. #熔断
  50. - name: Hystrix
  51. args:
  52. name: fallback
  53. #降级时返回的路径
  54. fallbackUri: forward:/fallback
  55. # # 路由标识(id:标识,具有唯一性)
  56. # - id: user-provider-api
  57. # #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
  58. # uri: lb://nacos-provider
  59. # #优先级,越小越优先
  60. # #order: 999
  61. # #路由条件(predicates:断言)
  62. # predicates:
  63. # # 路径匹配,
  64. # - Path=/bb/**
  65. # filters:
  66. # #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转发到目标服务的路径为/foo
  67. # #前缀过滤,请求地址:http://localhost:8084/usr/hello
  68. # #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
  69. # #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者service-client来说,不需要这段地址,所以需要去掉
  70. # - StripPrefix=1
  71. #自定义动态路由配置
  72. gateway:
  73. nacos:
  74. server-addr: ${spring.cloud.nacos.discovery.server-addr}
  75. # namespace: xxx-xx-xx-xx
  76. data-id: gateway.json
  77. group: DEFAULT_GROUP

    4.3 服务器降级响应处理


  
  1. package com.jmh.nacos_gateway.controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. /**
  7. * @author 蒋明辉
  8. * @data 2022/11/9 18:01
  9. */
  10. @RestController
  11. public class HystrixController {
  12. @RequestMapping("/fallback")
  13. public Object fallback (){
  14. Map map= new HashMap();
  15. map.put( "code", "204");
  16. map.put( "msg", "服务降级了");
  17. return map;
  18. }
  19. }

转载:https://blog.csdn.net/m0_63300795/article/details/127775243
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场