Spring Cloud Gateway層限流實(shí)現(xiàn)過(guò)程
寫(xiě)在前面的話
- 高并發(fā)的三駕馬車(chē):緩存、降級(jí)、限流,這里僅僅說(shuō)限流
- 常用的限流算法有:計(jì)數(shù)器算法、固定窗口算法、滑動(dòng)窗口算法、漏桶算法、令牌桶算法;每種算法的特點(diǎn)和優(yōu)缺點(diǎn)這里不展開(kāi),比較適用的限流算法基本都會(huì)選擇令牌桶,并且這里基于Spring Cloud Gateway Redis本身默認(rèn)就是基于令牌桶算法實(shí)現(xiàn)
- 限流按照類(lèi)型分為:?jiǎn)螜C(jī)、分布式;
- 限流按照請(qǐng)求流量的路徑分為:nginx、gateway、微服務(wù)
- 如果僅僅使用于單機(jī)環(huán)境:谷歌guava的RateLimiter、(AtomicInteger、Semaphore)【AQS】都是可以選擇的; 但是在說(shuō)到高并發(fā)基本已經(jīng)是分布式環(huán)境,此時(shí)的常用方案可以基于nginx的ngx_http_limit_req_module模塊、 gateway層基于 redis(底層使用lua腳本)、阿里的sentinel(需要單獨(dú)搭建服務(wù))等方案
- 該方案選擇使用spring cloud gateway RequestRateLimiter進(jìn)行限流,可選擇的策略有:基于訪問(wèn)的ip進(jìn)行控制,基于訪問(wèn)的請(qǐng)求參數(shù)進(jìn)行控制(前提是接口需要有對(duì)應(yīng)的訪問(wèn)參數(shù)),基于actuator實(shí)時(shí)監(jiān)控進(jìn)行控制(比如服務(wù)器的cpu達(dá)到80%),基于hystrix配置的策略進(jìn)行控制等
當(dāng)前選擇的方案在是 Spring Cloud Gateway層使用 redis作為分布式的協(xié)作中心,默認(rèn)底層使用令牌桶方式實(shí)現(xiàn),一定要清楚,當(dāng)前在這個(gè)位置:
如果再細(xì)分一下,當(dāng)前位于gateway的內(nèi)部的這個(gè)位置:
實(shí)現(xiàn)(這個(gè)省略了gateway的搭建和配置等):
1、在gateway網(wǎng)關(guān)服務(wù)進(jìn)入redis的pom
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> <version>2.3.2.RELEASE</version> </dependency>
2、yml配置
spring: application: name: kevin-gateway redis: host: 82.156.54.7 port: 6379 password: 123456 cloud: nacos: discovery: server-addr: 82.156.54.7:8848 gateway: globalcors: cors-configurations: '[/**]': allowCredentials: true allowedOrigins: "*" allowedMethods: "*" allowedHeaders: "*" add-to-simple-url-handler-mapping: true # 默認(rèn)過(guò)濾器(對(duì)所有route均生效) default-filters: # 請(qǐng)求限速配置 - name: RequestRateLimiter args: # 如果keyResolver返回空key,則拒絕該請(qǐng)求403,默認(rèn)true表示拒絕,false則表示允許訪問(wèn) deny-empty-key: false # 令牌桶算法每秒補(bǔ)充的token數(shù)量(每秒的請(qǐng)求數(shù)量)spring-boot-starter-data-redis-reactive redis-rate-limiter.replenishRate: 10 # 令牌桶算法token最大數(shù)量(每秒的最大請(qǐng)求數(shù)量) redis-rate-limiter.burstCapacity: 15 # 單次請(qǐng)求消費(fèi)的token數(shù)量 redis-rate-limiter.requestedTokens: 10 # 自定義的KeyResolver(從請(qǐng)求exchange解析id,用于區(qū)分限流的獨(dú)立單元,如用戶(hù)ID、remoteAddr、sessionId等) key-resolver: "#{@ipKeyResolver}" routes: - id: mosty-base uri: lb://mosty-base predicates: - Path=/mosty-base/** filters: - StripPrefix=1 - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 redis-rate-limiter.requestedTokens: 10 key-resolver: "#{@ipKeyResolver}" - id: mosty-search uri: lb://MOSTY-SEARCH predicates: - Path=/mosty-search/** filters: - StripPrefix=2
如上,首先需要進(jìn)入redis的配置
其次配置限流的Filter配置信息,允許配置全局過(guò)濾器對(duì)所有的route生效,也可以根據(jù)需求對(duì)每個(gè)route進(jìn)行單獨(dú)配置
配置參數(shù)說(shuō)明:
- name必須寫(xiě) RequestRateLimiter
args參數(shù):
- redis-rate-limiter.replenishRate:發(fā)送令牌的速率
- redis-rate-limiter.burstCapacity: 令牌桶的容量
- reids-rate-limiter.requestedTokens: 每個(gè)請(qǐng)求耗費(fèi)的令牌數(shù)
- key-resolver:如上所示是配置的一個(gè)spring bean的名稱(chēng),如果沒(méi)有配置則會(huì)獲取到KeyResolver的默認(rèn)實(shí)現(xiàn)PrincipalNameKeyResolver,并且訪問(wèn)接口都會(huì)返回 http 403狀態(tài)碼(與下面的deny-empty-key值相關(guān))
- deny-empty-key: false 如果keyResolver返回空key,則拒絕該請(qǐng)求403,默認(rèn)true表示拒絕,false則表示允許訪問(wèn)
1、ip限流策略
yml中配置 keyResolver:"#{@ipKeyResolver}"
@Bean(name = "ipKeyResolver") public KeyResolver ipKeyResolver() { return new KeyResolver() { @Override public Mono<String> resolve(ServerWebExchange exchange) { String hostName = Objects.requireNonNull(exchange.getRequest() .getRemoteAddress()).getHostName(); System.out.println("hostName:" + hostName); return Mono.just(hostName); } }; }
基于限流策略,正常訪問(wèn)的效果,以及被限流的效果(返回標(biāo)準(zhǔn)的Http 429編碼,Too Many Request)
2、請(qǐng)求參數(shù)限流策略
yml中配置 keyResolver:"#{@userKeyResolver}"
@Bean public KeyResolver userKeyResolver() { return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("userId"))); }
在下面的微服務(wù)的接口中一定要有該參數(shù),即需要能在請(qǐng)求參數(shù)中獲取到該值
@GetMapping("/getUserNameByUserId") public String userInfo(@RequestParam("userId") String userId) { // 查詢(xún)數(shù)據(jù)庫(kù)獲取 return "user name of" + userId; }
3、請(qǐng)求路徑(即接口)限流
yml中配置 keyResolver:"#{@requestPathKeyResolver}"
@Bean("requestPathKeyResolver") public KeyResolver requestPathKeyResolver() { return exchange -> Mono.just(Objects.requireNonNull( exchange.getRequest().getURI().())); }
4、基于hystrix熔斷進(jìn)行限流策略
基于上面的引入pom:spring-boot-starter-data-redis-reactive外,還需要引入
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
將Hystrix配置為全局的過(guò)濾器(對(duì)所有的 route生效),如下
說(shuō)明:
name執(zhí)行過(guò)濾器的類(lèi)型,指向了 Hystrix過(guò)濾器
args:default-filters的Hystrix將會(huì)使用HystrixCommand打包剩余的過(guò)濾器,并命名為fallbackcmd,我們還配置了可選的參數(shù)fallbackUri,降級(jí)邏輯被調(diào)用,請(qǐng)求將會(huì)被轉(zhuǎn)發(fā)到URI為/fallbackcontroller的控制器處理
spring: cloud: gateway: # 默認(rèn)過(guò)濾器(對(duì)所有route均生效) default-filters: - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallbackcontroller
添加 Hystrix的fallback的控制器接口方法
此時(shí)需要添加一個(gè)fallback的接口,并且返回想要的數(shù)據(jù)結(jié)構(gòu)
@ResponseBody @RequestMapping(value = "/fallbackcontroller") public ResponseResult<Object> fallBackController() { return ResponseResult.fail(HttpStatus.TOO_MANY_REQUESTS.value(), "超時(shí)限流", null); }
生效還需要配置hystrix的超時(shí)時(shí)間(yml配置如下):
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds
并且記得在啟動(dòng)類(lèi)上添加Hystrix的啟動(dòng)項(xiàng)
@EnableHystrix
為了實(shí)現(xiàn)效果此時(shí)將待訪問(wèn)的接口直接進(jìn)行sleep 5s,測(cè)試效果
@Slf4j @RestController public class TestController { @RequestMapping("/test/fallback") public Object fallacak() throws InterruptedException { Thread.sleep(5000); log.info("熔斷處理!??!"); return "Service Error?。?!"; } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)微信小程序登錄態(tài)維護(hù)的示例代碼
本篇文章主要介紹了java實(shí)現(xiàn)微信小程序登錄態(tài)維護(hù)的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09Intellij IDEA 配置Subversion插件實(shí)現(xiàn)步驟詳解
這篇文章主要介紹了Intellij IDEA 配置Subversion插件實(shí)現(xiàn)步驟詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05關(guān)于SpringBoot單元測(cè)試(cobertura生成覆蓋率報(bào)告)
這篇文章主要介紹了關(guān)于SpringBoot單元測(cè)試(cobertura生成覆蓋率報(bào)告),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11SpringBoot單元測(cè)試沒(méi)有執(zhí)行的按鈕問(wèn)題及解決
這篇文章主要介紹了SpringBoot單元測(cè)試沒(méi)有執(zhí)行的按鈕問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01jmeter中json提取器如何提取多個(gè)參數(shù)值
關(guān)于jmeter中的正則表達(dá)式及json提取器可以提取響應(yīng)值,但是實(shí)際可以需要上個(gè)接口的多個(gè)響應(yīng)值,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下2021-11-11Idea導(dǎo)入多個(gè)maven項(xiàng)目到同一目錄下的方法示例
這篇文章主要介紹了Idea導(dǎo)入多個(gè)maven項(xiàng)目到同一目錄下,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Springboot使用filter對(duì)response內(nèi)容進(jìn)行加密方式
這篇文章主要介紹了Springboot使用filter對(duì)response內(nèi)容進(jìn)行加密方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03