Zuul:限流
充當的是API網關的角色,每個請求都會經過他,所以很適合在上面做API保護,防止攻擊,比如某個API是發短信的,可能需要限制客戶端的請求速率,一定程度上抵制短信轟炸攻擊,降低損失,速度限流是放到前置過濾里面去做的,更具體一點來說,時機是在轉發之前調用,如果是前置過濾器有多個操作,限流要放到最靠前面的地方,比如Zuul的過濾器里面有限流,又有鑒權,那么限流應該早于限權,限流的方案很多,這里我也不列舉太多了,我就拿一種來說,也是我們要實現的,也是比較廣泛的,叫令牌桶限流,中間就是一個桶,里面是用來裝令牌的,叫令牌桶,這上面會以一定的速率,往這里面添加令牌,可能是幾秒鐘放一個,如果已經放滿了,放不下了,就會丟掉,WEB請求過來,會從令牌桶里面獲取令牌,拿到令牌之后,才可以繼續往下走,如果你連令牌都拿不到的話,那就直接被拒絕,我一看到這個令牌桶算法呢,我就覺得和我們現實生活中,買房的例子特別像的,怎么像呢,這里面就是開發商放出來有多少套房,以一定的速率放滿,那可能這個月放200套放進來,過兩個月再放一些,那你這群人從這里過來,開始買房,那你要先獲取令牌,這個令牌無非就是現在的條件,有的要3成,有的要你全款,當然這是比較傷心的一件事了,全款的往里走,按揭的不要進來,這個令牌就是一個資格,你有了這個資格,那么就可以繼續往下走
你就可以買房了,沒有的話很抱歉,繼續努力,現在我們回到程序來實現這個功能,還是新建一個filter,RateFilter extends ZuulFilter令牌桶算法已經有一個開源的組件了,GOOGLE開源的,在guava組件就有令牌桶的實現,他的類名叫做RateLimiter,我們可以點進來看<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>18.0</version>
</dependency>com.google.common.util.concurrent.RateLimiter有一個create,看一下參數,參數也寫的特別明白了private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);/**
* Creates a {@code RateLimiter} with the specified stable throughput, given as
* "permits per second" (commonly referred to as <i>QPS</i>, queries per second).
*
* <p>The returned {@code RateLimiter} ensures that on average no more than {@code
* permitsPerSecond} are issued during any given second, with sustained requests
* being smoothly spread over each second. When the incoming request rate exceeds
* {@code permitsPerSecond} the rate limiter will release one permit every {@code
* (1.0 / permitsPerSecond)} seconds. When the rate limiter is unused,
* bursts of up to {@code permitsPerSecond} permits will be allowed, with subsequent
* requests being smoothly limited at the stable rate of {@code permitsPerSecond}.
*
* @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in
* how many permits become available per second
* @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero
*/
// TODO(user): "This is equivalent to
// {@code createWithCapacity(permitsPerSecond, 1, TimeUnit.SECONDS)}".
public static RateLimiter create(double permitsPerSecond) {這里面的值就是你每秒鐘,要往里面放多少個令牌,假設是100好了,每秒鐘往里面放100個令牌,限流肯定是在所有優先級里面是最高的/*** filter類型** @return*/
@Override
public String filterType() {return PRE_TYPE;
}這就到順序了,順序目前哪個優先級是最高,我們要比他定義的優先級比他還要高,這里有一個-1,有一個-3,-3是優先級里面最高的,所以我們比-3還要小/*** filter order for {@link org.springframework.cloud.netflix.zuul.filters.pre.ServletDetectionFilter#filterOrder()}*/
int SERVLET_DETECTION_FILTER_ORDER = -3;我們還需要減1@Override
public int filterOrder() {return SERVLET_DETECTION_FILTER_ORDER - 1;
}run就是我們具體的實現了,如果沒有拿到令牌的話,看一下這個方法/**
* Acquires a permit from this {@link RateLimiter} if it can be acquired immediately without
* delay.
*
* <p>
* This method is equivalent to {@code tryAcquire(1)}.
*
* @return {@code true} if the permit was acquired, {@code false} otherwise
* @since 14.0
*/
public boolean tryAcquire() {
return tryAcquire(1, 0, MICROSECONDS);
}一般我們可以不用填參數,不用參數我們進來看一下,其實就是去取一個令牌,如果沒有取到的話,如果沒有拿到令牌,你就返回結果,跟之前的差不多吧,之前是返回沒有權限,沒有令牌就不是權限的事了,你類似于就可以這么返回/*** 實現filter邏輯** @return*/
@Override
public Object run() {//判斷--獲取通行令牌-->如果沒有令牌不等于之前的沒有權限401,可以拋出自定義異常或者其他處理if (!RATE_LIMITER.tryAcquire()) {requestContext.setSendZuulResponse(false);requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());}return null;
}我們也可以直接拋一個異常,我就演示另外一種,我就可以拋一個異常,之前叫RateFilter,還是不太合適,我們還是叫RateLimitFilter,Rate是速率,RateLimit才是限流,統一一下,這樣子就完成了限流,代碼量其實很少,這主要還是得益于GOOGLE這個組件,這個令牌桶算法他已經給我們寫好了,所以這里用起來的時候就特別方便,用到的地方就兩行代碼,除此之外我在github上也看到一個項目https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit限流感覺也不錯,大家可以來看一下,這個項目是國外一個小伙子開發的,我們看一下他的用法,也是先引入依賴你可以看到groupId<dependency><groupId>com.marcosbarbero.cloud</groupId><artifactId>spring-cloud-zuul-ratelimit</artifactId><version>LATEST</version>
</dependency>這不是Spring Cloud官方的,是個人的,雖然他項目名是以Spring Cloud開頭,這里他支持很多種限流的儲存,有Redis,Consul,還有JPA,以及內存,用法呢這里是他一系列的配置zuul:ratelimit:key-prefix: your-prefixenabled: truerepository: REDISbehind-proxy: trueadd-response-headers: truedefault-policy-list: #optional - will apply unless specific policy exists- limit: 10 #optional - request number limit per refresh interval windowquota: 1000 #optional - request time limit per refresh interval window (in seconds)refresh-interval: 60 #default value (in seconds)type: #optional- user- origin- url- httpmethodpolicy-list:myServiceId:- limit: 10 #optional - request number limit per refresh interval windowquota: 1000 #optional - request time limit per refresh interval window (in seconds)refresh-interval: 60 #default value (in seconds)type: #optional- user- origin- url- type: #optional value for each type- user=anonymous- origin=somemachine.com- url=/api #url prefix- role=user- httpmethod=get #case insensitive下面也給出了具體的用法
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.learn.cloud</groupId><artifactId>api-gateway</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><parent><groupId>cn.learn</groupId><artifactId>microcloud02</artifactId><version>0.0.1</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties> <dependencies><!-- <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency> --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId></dependency><!-- <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka-server</artifactId></dependency> --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zuul</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>18.0</version></dependency></dependencies><!-- 這個插件,可以將應用打包成一個可執行的jar包 --><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
package com.learn.cloud.filter;import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVLET_DETECTION_FILTER_ORDER;import org.springframework.stereotype.Component;import com.google.common.util.concurrent.RateLimiter;
import com.learn.cloud.exceptions.RateLimiterException;
import com.netflix.zuul.ZuulFilter;/*** 限流*/
@Component
public class RateLimiterFilter extends ZuulFilter {//create 每秒放入100個令牌private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);/*** filter類型** @return*/@Overridepublic String filterType() {return PRE_TYPE;}/*** filter執行順序,值越小優先級越高* 官方推薦使用x-1方式優先排序* 選擇最高優先級SERVLET_DETECTION_FILTER_ORDER,并-1** @return*/@Overridepublic int filterOrder() {return SERVLET_DETECTION_FILTER_ORDER - 1;}/*** filter 開啟關閉** @return*/@Overridepublic boolean shouldFilter() {return true;}/*** 實現filter邏輯** @return*/@Overridepublic Object run() {//判斷--獲取通行令牌-->如果沒有令牌不等于之前的沒有權限401,可以拋出自定義異常或者其他處理if (!RATE_LIMITER.tryAcquire()) {throw new RateLimiterException();}return null;}
}
package com.learn.cloud.exceptions;public class RateLimiterException extends RuntimeException {
}
?
總結
- 上一篇: Zuul:Pre和Post过滤器(下)
- 下一篇: SpringCloud_Sell.sql