您好,登錄后才能下訂單哦!
網(wǎng)關(guān)經(jīng)常需要對路由請求進(jìn)行過濾,進(jìn)行一些操作,如鑒權(quán)之后構(gòu)造頭部之類的,過濾的種類很多,如增加請求頭、增加請求 參數(shù) 、增加響應(yīng)頭和斷路器等等功能,這就用到了Spring Cloud Gateway 的 Filter。
當(dāng)我們有很多個(gè)服務(wù)時(shí),比如下圖中的user-service、goods-service、sales-service等服務(wù),客戶端請求各個(gè)服務(wù)的Api時(shí),每個(gè)服務(wù)都需要做相同的事情,比如鑒權(quán)、限流、日志輸出等。
對于這樣重復(fù)的工作,可以在微服務(wù)的上一層加一個(gè)全局的權(quán)限控制、限流、日志輸出的Api Gateway服務(wù),然后再將請求轉(zhuǎn)發(fā)到具體的業(yè)務(wù)服務(wù)層。這個(gè)Api Gateway服務(wù)就是起到一個(gè)服務(wù)邊界的作用,外接的請求訪問系統(tǒng),必須先通過網(wǎng)關(guān)層。
Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么豐富,它只有兩個(gè):“pre” 和 “post”。
Spring Cloud Gateway 的 Filter 從作用范圍可分為另外兩種GatewayFilter 與 GlobalFilter。
過濾器允許以某種方式修改傳入的HTTP請求或傳出的HTTP響應(yīng)。過濾器的作用域?yàn)樘囟酚?。Spring Cloud Gateway包含許多內(nèi)置的GatewayFilter工廠。
官方文檔都給出了這些過濾器工廠詳細(xì)的使用案例,在這里我們講解2個(gè)來演示下使用。
application.yml如下:
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://httpbin.org:80/get
filters:
- AddRequestHeader=X-Request-Foo, Bar
predicates:
- Method=GET
過濾器工廠會(huì)在匹配的請求頭加上一對請求頭,名稱為X-Request-Foo,值為Bar。
###RewritePath GatewayFilter Factory
在Nginx服務(wù)啟中有一個(gè)非常強(qiáng)大的功能就是重寫路徑,Spring Cloud Gateway默認(rèn)也提供了這樣的功能,這個(gè)功能是Zuul沒有。
application.yml如下:
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: http://httpbin.org
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?<segment>.*), /$\{segment}
所有的/foo/**開始的路徑都會(huì)命中配置的router。
請求http://httpbin.org/foo/get ,會(huì)轉(zhuǎn)到http://httpbin.org/get,結(jié)果如下:
{
"args": {
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8",
"Cache-Control": "max-age=0",
"Cookie": "Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1550127915; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%22168eada0ded53b-0d5d8c3ba9b7a2-10316653-1296000-168eada0dee3ad%22%2C%22%24device_id%22%3A%22168eada0ded53b-0d5d8c3ba9b7a2-10316653-1296000-168eada0dee3ad%22%2C%22props%22%3A%7B%7D%7D; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1",
"Forwarded": "proto=http;host=\"127.0.0.1:8080\";for=\"127.0.0.1:62278\"",
"Host": "httpbin.org",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36",
"X-Forwarded-Host": "127.0.0.1:8080",
"X-Forwarded-Prefix": "/foo"
},
"origin": "127.0.0.1, 124.74.78.150, 127.0.0.1",
"url": "https://127.0.0.1:8080/get"
}
Spring Cloud Gateway內(nèi)置了的過濾器工廠,足夠是大部分場景使用,而且我們可以實(shí)現(xiàn)GatewayFilter和Ordered 這兩個(gè)接口來自定義過濾器。代碼如下:
/**
* 統(tǒng)計(jì)某個(gè)或者某種路由的處理時(shí)長
*/
public class CustomerGatewayFilter implements GatewayFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilter.class );
private static final String COUNT_START_TIME = "countStartTime";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put(COUNT_START_TIME, Instant.now().toEpochMilli() );
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
long startTime = exchange.getAttribute(COUNT_START_TIME);
long endTime=(Instant.now().toEpochMilli() - startTime);
log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms");
})
);
}
@Override
public int getOrder() {
return 0;
}
}
上述代碼中,getOrder()方法是來給過濾器設(shè)定優(yōu)先級別的,值越大則優(yōu)先級越低。需要將自定義的GatewayFilter 注冊到router中,代碼如下:
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/customer/**")
.filters(f -> f.filter(new CustomerGatewayFilter())
.addResponseHeader("X-Response-test", "test"))
.uri("http://httpbin.org:80/get")
.id("customer_filter_router")
)
.build();
}
使用 curl 測試,命令行輸入:
curl http://localhost:8080/customer/555
控制臺輸出如下:
2019-02-22 10:39:47.840 INFO 1310 --- [ctor-http-nio-3] com.gf.config.CustomerGatewayFilter : /customer/555: 314ms
自定義GatewayFilter又有兩種實(shí)現(xiàn)方式,一種是上面的直接 實(shí)現(xiàn)GatewayFilter接口,另一種是 自定義過濾器工廠(繼承AbstractGatewayFilterFactory類) , 選擇自定義過濾器工廠的方式,可以在配置文件中配置過濾器了。
@Component
public class CustomerGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomerGatewayFilterFactory.Config> {
private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilterFactory.class );
private static final String COUNT_START_TIME = "countStartTime";
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enabled");
}
public CustomerGatewayFilterFactory() {
super(Config.class);
log.info("Loaded GatewayFilterFactory [CustomerGatewayFilterFactory]");
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
exchange.getAttributes().put(COUNT_START_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(COUNT_START_TIME);
if (startTime != null) {
StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())
.append(": ")
.append(System.currentTimeMillis() - startTime)
.append("ms");
sb.append(" params:").append(exchange.getRequest().getQueryParams());
log.info(sb.toString());
}
})
);
};
}
public static class Config {
/**
* 控制是否開啟統(tǒng)計(jì)
*/
private boolean enabled;
public Config() {}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
application.yml 中 網(wǎng)關(guān)路由配置如下:
spring:
profiles: elapse_route
cloud:
gateway:
routes:
- id: elapse_route
uri: http://httpbin.org:80/get
filters:
- Customer=true
predicates:
- Method=GET
使用 curl 測試,命令行輸入:
curl http://localhost:8080/customer?foo=1
控制臺輸出如下:
2019-02-23 13:37:08.769 INFO 1700 --- [ctor-http-nio-3] c.g.config.CustomerGatewayFilterFactory : /customer: 585ms params:{foo=[1]}
Spring Cloud Gateway框架內(nèi)置的GlobalFilter如下:
內(nèi)置的 GlobalFilter 能夠滿足大多數(shù)的需求了,但是如果遇到特殊情況,內(nèi)置滿足不了我們的需求,還可以自定義GlobalFilter。
下面的我們自定義一個(gè)GlobalFilter,去校驗(yàn)所有請求的請求參數(shù)中是否包含“token”,如何不包含請求參數(shù)“token”則不轉(zhuǎn)發(fā)路由,否則執(zhí)行正常的邏輯。
/**
* Token 校驗(yàn)全局過濾器
*/
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger( AuthorizeFilter.class );
private static final String AUTHORIZE_TOKEN = "token";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst( AUTHORIZE_TOKEN );
if ( StringUtils.isBlank( token )) {
log.info( "token is empty ..." );
exchange.getResponse().setStatusCode( HttpStatus.UNAUTHORIZED );
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
使用 curl 測試,命令行輸入:
curl http://localhost:8080/customer?foo=1
控制臺輸出如下:
token is empty ...
源碼 : https://github.com/gf-huanchupk/SpringCloudLearning/tree/master/chapter13/springcloud-gateway-filter
歡迎關(guān)注我的公眾號《程序員果果》,關(guān)注有驚喜~~
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。