概述:在SpringCloud中我们之前在使用Hystrix 以及 sentinel 中都可以对服务调用进行限流控制,在SpringCloud体系中通过网关也可以对整个系统架构进行限流处理。本章将介绍一些通过微服务网关实现限流的一些常见算法以及其在SpringCloud Gateway 网关下的实现方式。

一、常见的限流算法

1,计数器算法

计数器算法为最简答的限流算法,其实现原理是为维护一个单位时间内的计数器。在单位时间内,开始计数器为0,每次通过一个请求计数器+1。如果单位时间内 计数器的数量大于了预先设定的阈值,则在此刻到单位时间的最后一刻范围内的请求都将被拒绝。单位时间结束计数器归零,重新开始计数。

2,漏桶算法

漏桶算法实际为一个容器请求队列,关键要素为 桶大小(队列大小),流出速率(出队速率)。即无论请求并发多高,如果桶内的队列满了,多余进来的请求都将被舍弃。由于桶的流出速率固定,所以可以保证限流后的请求并发数可以固定在一个范围内。

3,令牌桶算法

令牌桶算法为漏桶算法的一种改进。漏桶算法能够控制调用服务的速率,而令牌桶算法不仅能控制调用服务的速率,还能在短时间内允许一个超并发的调用。其实现原理为,存在一个令牌桶,并且有一个持续不断地产生令牌的机制,比如每秒产生100个令牌。桶存在一个固定大小,比如300。当桶中的令牌满了的时候,多余的令牌将被舍弃。

当请求过来时必须先从桶中获取一个令牌,桶内令牌数减一,获取到令牌的请求将被放行。桶中令牌被用光时,没有获取到令牌的请求将进行等待或者拒绝。这样在短期的时间内该算法将允许大于100,小于等于300的并发。如果持续有大于100的并发请求经过网关,在消耗完桶内令牌后,则最大通过网关的qps为产生令牌的速率,及 qps=100。

二、限流算法的简单实现方式

1,使用Filter 实现令牌桶算法(RequestRateLimiter局部过滤器,需要借助redis+lua脚本实现

  1. 修改pom 添加redis依赖
  2. 添加reids key 的解析器即key-resolver 解析类
  3. 调整配置文件

pom.xml 需要添加redis依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency>

解析类src/main/java/com/xiaohui/gateway/config/KeyResolverConfiguration.java

package com.xiaohui.gateway.config;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Configuration
public class KeyResolverConfiguration {@Beanpublic KeyResolver pathKeyResolver(){return new KeyResolver() {@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {return Mono.just(exchange.getRequest().getPath().toString());}};}
}

application.properties 中主要部分

spring:application:name: api-gateway-server #服务名称redis:host: 127.0.0.1pool: 6379database: 0cloud:gateway:routes:#配置路由: 路由id,路由到微服务的uri,断言(判断条件)- id: product-service #保持唯一#uri: http://127.0.0.1:8001 #目标为服务地址uri: lb://cloud-payment-service # lb:// 根据服务名称从注册中心获取请求地址路径predicates:#- Path=/payment/** #路由条件 path 路由匹配条件- Path=/product-service/** #给服务名称前加上一个固定的应用分类路径 将该路径转发到 http://127.0.0.1:8001/payment/get/1filters: #配置路由过滤器  http://127.0.0.1:8080/product-service/payment/get/1 -> http://127.0.0.1:8001/payment/get/1- name: RequestRateLimiterargs:#使用SpEL从容器中获取对象key-resolver: '#{@pathKeyResolver}'#桶令牌每秒产生平均速率redis-rate-limiter.replenishRate: 1#令牌桶的上限redis-rate-limiter.burstCapacity: 2- RewritePath=/product-service/(?<segment>.*),/$\{segment} #路径重写的过滤器,在yml中$写为 $\

如果需要进行根据判断参数(userId)进行设置限流则其解析类可以调整为如下示例(修改配置文件中key-resolver 项为 paramKeyResolver):

package com.xiaohui.gateway.config;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Configuration
public class KeyResolverConfiguration {@Beanpublic KeyResolver paramKeyResolver(){return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));}
}

效果:每秒钟一次请求http://127.0.0.1:8080/product-service/payment/get/1?userId=11 没问题,如果连续每秒钟多次请求改地址,则部分请求会出现返回429错误码。

2,使用Sentinel 实现限流

在Gateway + Sentinel的实现限流方案中,有两种限流方式:

  1. 基于 route-id 的ruote维度:即在application.yml 文件中的spring.cloud.gateway.routes[0].id配置项
  2. 基于自定义API维度:通过 ApiDefinition 定义不同的分组,进行分组限流控制

使用Gateway+Sentinel的形式 都需要添加Sentinel的依赖,依赖坐标如下:

        <!-- 使用sentinel对gateway进行限流实现 --><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-spring-cloud-gateway-adapter</artifactId><version>1.6.3</version></dependency>

实现方式一:基于route-id的形式

该种配置方式将对配置的路由id中的微服务中全部接口进行限流控制。无法进行单独的限流控制。

package com.xiaohui.gateway.config;import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.*;@Configuration
public class GatewayConfiguration {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolverProvider,ServerCodecConfigurer serverCodecConfigurer){this.viewResolvers = viewResolverProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}/*** 配置限流的异常处理器* @return*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(){return new SentinelGatewayBlockExceptionHandler(this.viewResolvers,this.serverCodecConfigurer);}/*** 配置限流过滤器* @return*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public GlobalFilter sentinelGlobalFilter(){return new SentinelGatewayFilter();}/*** product-service: rules-id配置项值* setIntervalSec : 单位时间* setCount       : 调用总数*/@PostConstructpublic void initGatewayRules(){Set<GatewayFlowRule> rules = new HashSet<>();// 按照 rules-id配置项值 整体设置限流rules.add(new GatewayFlowRule("product-service").setIntervalSec(1).setCount(1));GatewayRuleManager.loadRules(rules);}/*** 自定义限流处理器*/@PostConstructpublic void initBlockHanlers(){BlockRequestHandler blockhandler = new BlockRequestHandler() {@Overridepublic Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {Map map = new HashMap();map.put("code","001");map.put("msg","网关限流拦截返回...");return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromObject(map));}};GatewayCallbackManager.setBlockHandler(blockhandler);}}

对应的application.yml 如下:

server:port: 8080
spring:application:name: api-gateway-server #服务名称redis:host: 127.0.0.1pool: 6379database: 0cloud:gateway:routes:#配置路由: 路由id,路由到微服务的uri,断言(判断条件)- id: product-service #保持唯一#uri: http://127.0.0.1:8001 #目标为服务地址uri: lb://cloud-payment-service # lb:// 根据服务名称从注册中心获取请求地址路径predicates:#- Path=/payment/** #路由条件 path 路由匹配条件- Path=/product-service/** #给服务名称前加上一个固定的应用分类路径 将该路径转发到 http://127.0.0.1:8001/payment/get/1filters: #配置路由过滤器  http://127.0.0.1:8080/product-service/payment/get/1 -> http://127.0.0.1:8001/payment/get/1- RewritePath=/product-service/(?<segment>.*),/$\{segment} #路径重写的过滤器,在yml中$写为 $\# 配置自动根据微服务名称进行路由转发 http://127.0.0.1:8080/cloud-payment-service/payment/get/1discovery:locator:enabled: true #开启根据服务名称自动转发lower-case-service-id: true #微服务名称已小写形式呈现#eureka 注册中心
eureka:client:register-with-eureka: truefetch-registry: trueservice-url:defaultZone: http://eureka1.com:9000/eureka/instance:prefer-ip-address: true #使用ip进行注册

该配置类中在initGatewayRules 方法中定义了需要限流的服务id product-service  对应的微服务  uri: lb://cloud-payment-service. initBlockHanlers方法定义了限流时对限流接口的返回信息封装。

实现方式二:基于自定义API维度

配置文件同实现方式一的,配置类如下:

package com.xiaohui.gateway.config;import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.*;@Configuration
public class GatewayConfiguration {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolverProvider,ServerCodecConfigurer serverCodecConfigurer){this.viewResolvers = viewResolverProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}/*** 配置限流的异常处理器* @return*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(){return new SentinelGatewayBlockExceptionHandler(this.viewResolvers,this.serverCodecConfigurer);}/*** 配置限流过滤器* @return*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public GlobalFilter sentinelGlobalFilter(){return new SentinelGatewayFilter();}/*** product-service: rules-id配置项值* setIntervalSec : 单位时间* setCount       : 调用总数*/@PostConstructpublic void initGatewayRules(){Set<GatewayFlowRule> rules = new HashSet<>();// 按照 rules-id配置项值 整体设置限流
//        rules.add(new GatewayFlowRule("product-service").setIntervalSec(1).setCount(1));//分组限流rules.add(new GatewayFlowRule("product_api").setIntervalSec(1).setCount(1));rules.add(new GatewayFlowRule("order_api").setIntervalSec(3).setCount(1));GatewayRuleManager.loadRules(rules);}/*** 自定义限流处理器*/@PostConstructpublic void initBlockHanlers(){BlockRequestHandler blockhandler = new BlockRequestHandler() {@Overridepublic Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {Map map = new HashMap();map.put("code","001");map.put("msg","网关限流拦截返回...");return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromObject(map));}};GatewayCallbackManager.setBlockHandler(blockhandler);}/*** 自定义限流分组* 1,定义分组* 2,对个组设置匹配路径 如:/product-service/***/@PostConstructprivate void initCustomizedApis(){Set<ApiDefinition> definitions = new HashSet<>();ApiDefinition api1 = new ApiDefinition("product_api").setPredicateItems(new HashSet<ApiPredicateItem>(){{add(new ApiPathPredicateItem().setPattern("/product-service/payment/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));add(new ApiPathPredicateItem().setPattern("/product-service/payment2/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));}});ApiDefinition api2 = new ApiDefinition("order_api").setPredicateItems(new HashSet<ApiPredicateItem>(){{add(new ApiPathPredicateItem().setPattern("/product-service/order/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));}});definitions.add(api1);definitions.add(api2);GatewayApiDefinitionManager.loadApiDefinitions(definitions);}
}

在 initCustomizedApis 方法中,定义了两个分组 "product_api"、"order_api",并分别配置了不同的匹配路径。

在initGatewayRules 方法中对不同的分组设定了不同的限流规则。单位时间和 阈值的指定。

SpringCloud 微服务网关Gateway常用限流算法以及简单实现相关推荐

  1. SpringCloud 微服务网关Gateway 动态路由配置

    概述:在上一章节<SpringCloud 微服务网关Gateway介绍及简单路由配置>中我们讲述了Gateway的最简单的路由配置方式.但是其中比较明显的问题就是我们在配置路由服务的地址时 ...

  2. SpringCloud—— 微服务网关GateWay

    目录 1.GateWay网关概述 1.1.什么是GateWay? 1.2.为什么要使用微服务网关? 1.3.Zuul与GateWay网关的区别? 2.快速入门 2.1.创建项目 2.2.配置yml文件 ...

  3. SpringCloud 微服务网关Gateway介绍及简单路由配置

    概述:什么是微服务网关?为了解决用户客户端在调用微服务系统中的多个消费者工程接口时,需要维护非常多的消费者应用接口地址等信息,以及可能存在不同应用见的调用跨域等问题,微服务网关组件随即出现.网关作为用 ...

  4. SpringCloud微服务组件:Sentinel限流熔断

    点击关注公众号,实用技术文章及时了解 前言 什么是雪崩问题? 微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况. 解决雪崩问题的常见方式有四种: 超时处理:设定超时时间,请 ...

  5. Spring Cloud之(十八)微服务网关GateWay

    十八.微服务网关GateWay Zuul 1.x 是一个基于阻塞 IO 的 API Gateway 以及 Servlet:直到 2018 年 5 月,Zuul 2.x(基于Netty,也是非阻塞的,支 ...

  6. 微服务网关GateWay 过滤+路由+限流

    文章目录 1 微服务网关概述 2 微服务网关微服务搭建 3 微服务网关跨域 4 微服务网关过滤器 5 微服务网关限流 5.1 思路分析 5.2 令牌桶算法 5.3 网关限流代码实现 1 微服务网关概述 ...

  7. Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

    微服务网关 一. Zuul网关 1. 创建工程 并导入依赖 2. application.yml 配置文件 3. 启动类添加注解 4. 依次启动服务 5. 进入浏览器访问测试 二. ZuulFilte ...

  8. 微服务之熔断、限流、降级 三板斧

    系列服务器开发 文章目录 系列服务器开发 前言 一.背景 二.熔断 三. 限流 四. 降级 五.三种措施的差异 总结 前言 Spring Cloud全家桶是提供的一整套微服务开源解决方案,包括服务注册 ...

  9. golang bufio.newscanner如何超时跳出_Golang微服务的熔断与限流

    (给Go开发大全加星标) 来源:Che Dan https://medium.com/@dche423/micro-in-action-7-cn-ce75d5847ef4 [导读]熔断和限流机制对于大 ...

  10. 微服务网关Gateway

    使用场景 不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题: 客户端会多次请求不同的微服务,增加了客户端 ...

最新文章

  1. linux jdk配置环境变量
  2. 第二次做HDOJ 1051
  3. 学业水平考试b能上985吗_211 和985 的大学是不是要求学业水平考试全部是A
  4. Windows 2012 - Dynamic Access Control 浅析
  5. 电力电子、电机控制系统的建模和仿真_清华团队研发,首款国产电力电子仿真软件来啦~已捐赠哈工大、海工大、清华使用!...
  6. csv文件python是怎么输入的,python怎么读取和写入csv文件
  7. 程维谈智慧交通:我们赶上好时代 走出了自己路
  8. Docker : Error response from daemon: Get https://docker.elastic.co/v2/: net/http: TLS handshake time
  9. leetcode64. 最小路径和
  10. 设计模式(外观模式)
  11. Parallels Toolbox for mac(PD工具箱合集)
  12. shell脚本中变量的赋值
  13. JavaScript计算器(加减乘除完善)
  14. 移动端分享链接给微信好友
  15. 管理信息系统期末测试题
  16. 如何让我的世界变成别人的世界(国际版)——HMCL启动器之如何同时用到mod和光影
  17. 基于百度paddle的快递面单三段码识别
  18. NS2中GOD的使用
  19. uni-app学习之旅(二)uni-app开发规范
  20. Edge浏览器安装插件报错:显示出现错误 Download interrupted

热门文章

  1. ubuntu系统Firefox浏览器B站视频无法播放
  2. Windows XP IIS 500内部错误 解决方案(HTTP 500内部服务器错误)
  3. 对大一c语言学习的感想
  4. 1987年,国际C语言混乱代码大赛
  5. 仿QQ音乐下载歌曲头部导航
  6. 机器学习资源备份,转载自大神https://zhuanlan.zhihu.com/p/26876504
  7. 时区提示:Local time zone must be set--see zic manual page 2018的解决办法
  8. 短网址还原的Bookmarklet
  9. 学java有前途吗?学会之后有什么好处?
  10. MAC 网速问题 变慢 的来看看 经验