作為Netflix Zuul的替代者,Spring Cloud Gateway是一款非常實(shí)用的微服務(wù)網(wǎng)關(guān),在Spring Cloud微服務(wù)架構(gòu)體系中發(fā)揮非常大的作用。本文對Spring Cloud Gateway常見使用場景進(jìn)行了梳理,希望對微服務(wù)開發(fā)人員提供一些幫助。
創(chuàng)新互聯(lián)建站專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站建設(shè)、網(wǎng)站設(shè)計、麻江網(wǎng)絡(luò)推廣、小程序設(shè)計、麻江網(wǎng)絡(luò)營銷、麻江企業(yè)策劃、麻江品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;創(chuàng)新互聯(lián)建站為所有大學(xué)生創(chuàng)業(yè)者提供麻江建站搭建服務(wù),24小時服務(wù)熱線:18980820575,官方網(wǎng)址:www.chinadenli.net
Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技術(shù)開發(fā)的網(wǎng)關(guān),Spring Cloud Gateway旨在為微服務(wù)架構(gòu)提供簡單、有效和統(tǒng)一的API路由管理方式,Spring Cloud Gateway作為Spring Cloud生態(tài)系統(tǒng)中的網(wǎng)關(guān),目標(biāo)是替代Netflix Zuul,其不僅提供統(tǒng)一的路由方式,并且還基于Filer鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全、監(jiān)控/埋點(diǎn)、限流等。
網(wǎng)關(guān)提供API全托管服務(wù),豐富的API管理功能,輔助企業(yè)管理大規(guī)模的API,以降低管理成本和安全風(fēng)險,包括協(xié)議適配、協(xié)議轉(zhuǎn)發(fā)、安全策略、防刷、流量、監(jiān)控日志等貢呢。一般來說網(wǎng)關(guān)對外暴露的URL或者接口信息,我們統(tǒng)稱為路由信息。如果研發(fā)過網(wǎng)關(guān)中間件或者使用過Zuul的人,會知道網(wǎng)關(guān)的核心是Filter以及Filter Chain(Filter責(zé)任鏈)。Sprig Cloud Gateway也具有路由和Filter的概念。下面介紹一下Spring Cloud Gateway中幾個重要的概念。
路由。路由是網(wǎng)關(guān)最基礎(chǔ)的部分,路由信息有一個ID、一個目的URL、一組斷言和一組Filter組成。如果斷言路由為真,則說明請求的URL和配置匹配
斷言。Java8中的斷言函數(shù)。Spring Cloud Gateway中的斷言函數(shù)輸入類型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的斷言函數(shù)允許開發(fā)者去定義匹配來自于http request中的任何信息,比如請求頭和參數(shù)等。

如上圖所示,Spring cloudGateway發(fā)出請求。然后再由Gateway Handler Mapping中找到與請求相匹配的路由,將其發(fā)送到Gateway web handler。Handler再通過指定的過濾器鏈將請求發(fā)送到我們實(shí)際的服務(wù)執(zhí)行業(yè)務(wù)邏輯,然后返回。
以Spring Boot框架開發(fā)為例,啟動一個Gateway服務(wù)模塊(以Consul作為注冊中心),一個后端服務(wù)模塊。client端請求經(jīng)gateway服務(wù)把請求路由到后端服務(wù)。
前提條件:
Consul:版本1.5.0。
Spring bot:版本2.1.5。
Spring cloud:版本Greenwich.SR1。
這里以使用Spring Boot框架開發(fā)微服務(wù)為例,啟動一個服務(wù)并注冊到Consul。
引入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>注冊服務(wù)到Consul,配置文件配置如下:
spring:
application:
name: service-consumer
cloud:
consul:
host: 127.0.0.1
port: 8500
discovery:
service-name: service-consumer如下定義RestController,發(fā)布HTTP接口。
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@GetMapping(value = "/info")
public User info() {
return userService.info();
}
}
注:此為服務(wù)端配置,經(jīng)Gateway把請求路由轉(zhuǎn)發(fā)到該服務(wù)上。
創(chuàng)建一個Gateway服務(wù),引入以下依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>啟動類配置如下:
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}Spring Cloud Gateway對client端請求起到路由功能,主要配置如下:
server:
port: 8098
spring:
application:
name: service-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
consul:
host: 127.0.0.1 #注冊gateway網(wǎng)關(guān)到consul
port: 8500
discovery:
service-name: service-gateway此時使用http://localhost:8089/service-consumer/user/info訪問服務(wù),網(wǎng)關(guān)即可對服務(wù)進(jìn)行路由轉(zhuǎn)發(fā),把請求轉(zhuǎn)發(fā)到具體后端服務(wù)上。此時,url中使用的url前綴service-consumer,是后端服務(wù)在Consul注冊的服務(wù)名稱轉(zhuǎn)為小寫字母以后的字符串。
本文第二部分開發(fā)規(guī)范中定義了網(wǎng)關(guān)進(jìn)行路由轉(zhuǎn)發(fā)的配置,除了上述配置方式還可以使用下面的方式進(jìn)行配置:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: service_consumer
uri: lb://service-consumer
predicates:
- Path= /consumer/**
filters:
- StripPrefix=1在上面的配置中,配置了一個Path的predicat,將以/consumer/**開頭的請求都會轉(zhuǎn)發(fā)到uri為lb://service-consumer的地址上,lb://service-consumer(注冊中心中服務(wù)的名稱)即service-consumer服務(wù)的負(fù)載均衡地址,并用StripPrefix的filter 在轉(zhuǎn)發(fā)之前將/consumer去掉。同時將spring.cloud.gateway.discovery.locator.enabled改為false,如果不改的話,之前的http://localhost:8081/service-consumer/user/info這樣的請求地址也能正常訪問,因?yàn)檫@時為每個服務(wù)創(chuàng)建了2個router。
本文第二部分和本節(jié)一共講述了兩種配置方式,兩種配置都可以實(shí)現(xiàn)請求路由轉(zhuǎn)發(fā)的功能。參數(shù)spring.cloud.gateway.discovery.locator.enabled為true,表明Gateway開啟服務(wù)注冊和發(fā)現(xiàn)的功能,并且Spring Cloud Gateway自動根據(jù)服務(wù)發(fā)現(xiàn)為每一個服務(wù)創(chuàng)建了一個router,這個router將以服務(wù)名開頭的請求路徑轉(zhuǎn)發(fā)到對應(yīng)的服務(wù)。spring.cloud.gateway.discovery.locator.lowerCaseServiceId是將請求路徑上的服務(wù)名配置為小寫(因?yàn)榉?wù)注冊的時候,向注冊中心注冊時將服務(wù)名轉(zhuǎn)成大寫的了)。
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
Spring Cloud Gateway還針對跨域訪問做了設(shè)計,可以使用以下配置解決跨域訪問問題:
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET
allowHeaders:
- Content-Type在上面的示例中,允許來自https://docs.spring.io的get請求進(jìn)行訪問,并且表明服務(wù)器允許請求頭中攜帶字段Content-Type。
Spring Cloud Gateway的filter生命周期不像Zuul那么豐富,它只有兩個:“pre”和“post”:
pre:這種過濾器在請求被路由之前調(diào)用。可以利用這個過濾器實(shí)現(xiàn)身份驗(yàn)證、在集群中選擇請求的微服務(wù)、記錄調(diào)試的信息。
Spring Cloud gateway的filter分為兩種:GatewayFilter和Globalfilter。GlobalFilter會應(yīng)用到所有的路由上,而Gatewayfilter將應(yīng)用到單個路由或者一個分組的路由上。
利用Gatewayfilter可以修改請求的http的請求或者是響應(yīng),或者根據(jù)請求或者響應(yīng)做一些特殊的限制。更多時候可以利用Gatewayfilter做一些具體的路由配置。
下面的配置是AddRequestParameter Gatewayfilter的相關(guān)配置。
spring:
application:
name: service-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: parameter_route
uri: http://localhost:8504/user/info
filters:
- AddRequestParameter=foo, bar
predicates:
- Method=GET上述配置中指定了轉(zhuǎn)發(fā)的地址,設(shè)置所有的GET方法都會自動添加foo=bar,當(dāng)請求符合上述路由條件時,即可在后端服務(wù)上接收到Gateway網(wǎng)關(guān)添加的參數(shù)。
另外再介紹一種比較常用的filter,即StripPrefix gateway filter。
配置如下:
spring:
cloud:
gateway:
routes:
- id: stripprefixfilter
uri: lb://service-consumer
predicates:
- Path=/consumer/**
filters:
- StripPrefix=1當(dāng)client端使用http://localhost:8098/consumer/user/info路徑進(jìn)行請求時,如果根據(jù)上述進(jìn)行配置Gateway會將請求轉(zhuǎn)換為http://localhost:8098/service-consumer/user/info。以此作為前端請求的最終目的地。
Gateway網(wǎng)關(guān)可以根據(jù)不同的方式進(jìn)行匹配進(jìn)而把請求分發(fā)到不同的后端服務(wù)上。
通過header進(jìn)行匹配,把請求分發(fā)到不同的服務(wù)上,配置如下:
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://baidu.com
predicates:
- Header=X-Request-Id, \d+通過curl測試:curl http://localhost:8080 -H "X-Request-Id:666666",返回頁面代碼證明匹配成功。
如果是以Host進(jìn)行匹配,配置如下:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://baidu.com
predicates:
- Host=**.baidu.com通過curl http://localhost:8098 -H "Host: www.baidu.com"進(jìn)行測試,返回頁面代碼即轉(zhuǎn)發(fā)成功。
可以通過POST、GET、PUT、DELTE等不同的方式進(jìn)行路由:
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://baidu.com
predicates:
- Method=GET通過 curl http://localhost:8098 進(jìn)行測試,返回頁面代碼即表示成功。
上述是單個匹配進(jìn)行路由,如果把多個匹配合在一起進(jìn)行路由,必須滿足所有的路有條件才會進(jìn)行路由轉(zhuǎn)發(fā)。
Spring Cloud Gateway也可以利用Hystrix的熔斷特性,在流量過大時進(jìn)行服務(wù)降級,同時項目中必須加上Hystrix的依賴。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency> 配置后,Gateway將使用fallbackcmd作為名稱生成HystrixCommand對象進(jìn)行熔斷處理。如果想添加熔斷后的回調(diào)內(nèi)容,需要添加以下配置:
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: lb://consumer-service
predicates:
- Path=/consumer/**
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
- StripPrefix=1
hystrix:
command:
fallbackcmd:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 #超時時間,若不設(shè)置超時時間則有可能無法觸發(fā)熔斷上述配置中給出了熔斷之后返回路徑,因此,在Gateway服務(wù)模塊添加/fallback路徑,以作為服務(wù)熔斷時的返回路徑。
@RestController
public class GatewayController {
@RequestMapping(value = "/fallback")
public String fallback(){
return "fallback nothing";
}
}fallbackUri: forward:/fallback配置了 fallback 時要會調(diào)的路徑,當(dāng)調(diào)用 Hystrix 的 fallback 被調(diào)用時,請求將轉(zhuǎn)發(fā)到/fallback這個 URI,并以此路徑的返回值作為返回結(jié)果。
通過簡單的配置,Spring Cloud Gateway就可以支持請求重試功能。
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://localhost:8504/user/info
predicates:
- Path=/user/**
filters:
- name: Retry
args:
retries: 3
status: 503
- StripPrefix=1Retry GatewayFilter通過四個參數(shù)來控制重試機(jī)制,參數(shù)說明如下:
retries:重試次數(shù),默認(rèn)值是 3 次。
statuses:HTTP 的狀態(tài)返回碼,取值請參考:org.springframework.http.HttpStatus。
methods:指定哪些方法的請求需要進(jìn)行重試邏輯,默認(rèn)值是 GET 方法,取值參考:org.springframework.http.HttpMethod。
使用上述配置進(jìn)行測試,當(dāng)后臺服務(wù)不可用時,會在控制臺看到請求三次的日志,證明此配置有效。
Spring Cloud Gateway本身集成了限流操作,Gateway限流需要使用Redis,pom文件中添加Redis依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>配置文件中配置如下:
spring:
cloud:
gateway:
routes:
- id: rate_limit_route
uri: lb://service-consumer
predicates:
- Path=/user/**
filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@hostAddrKeyResolver}"
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 3
- StripPrefix=1
consul:
host: 127.0.0.1
port: 8500
discovery:
service-name: service-gateway
instance-id: service-gateway-233
redis:
host: localhost
port: 6379在上面的配置問價中,配置了Redis的信息,并配置了RequestRateLimiter的限流過濾器,該過濾器需要配置三個參數(shù):
BurstCapacity:令牌桶的總?cè)萘俊?/p>
replenishRate:令牌通每秒填充平均速率。
注意:filter下的name必須是RequestRateLimiter。
Key-resolver參數(shù)后面的bean需要自己實(shí)現(xiàn),然后注入到Spring容器中。KeyResolver需要實(shí)現(xiàn)resolve方法,比如根據(jù)ip進(jìn)行限流,則需要用hostAddress去判斷。實(shí)現(xiàn)完KeyResolver之后,需要將這個類的Bean注冊到Ioc容器中。還可以根據(jù)uri限流,同hostname限流是一樣的。例如以ip限流為例,在gateway模塊中添加以下實(shí)現(xiàn):
public class HostAddrKeyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
public HostAddrKeyResolver hostAddrKeyResolver() {
return new HostAddrKeyResolver();
}
}把該類注入到spring容器中:
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public HostAddrKeyResolver hostAddrKeyResolver(){
return new HostAddrKeyResolver();
}
}基于上述配置,可以對請求基于ip的訪問進(jìn)行限流。
Spring Cloud Gateway內(nèi)置了過濾器,能夠滿足很多場景的需求。當(dāng)然,也可以自定義過濾器。在Spring Cloud Gateway自定義過濾器,過濾器需要實(shí)現(xiàn)GatewayFilter和Ordered這兩個接口。
下面的例子實(shí)現(xiàn)了Gatewayfilter,它可以以log日志的形式記錄每次請求耗費(fèi)的時間,具體實(shí)現(xiàn)如下:
public class RequestTimeFilter implements GatewayFilter, Ordered {
private static final Log log = LogFactory.getLog(GatewayFilter.class);
private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
if (startTime != null) {
log.info("請求路徑:"+exchange.getRequest().getURI().getRawPath() + "消耗時間: " + (System.currentTimeMillis() - startTime) + "ms");
}
})
);
}
@Override
public int getOrder() {
return 0;
}
}上述代碼中定義了自己實(shí)現(xiàn)的過濾器。Ordered的int getOrder()方法是來給過濾器定優(yōu)先級的,值越大優(yōu)先級越低。還有一個filter(ServerWebExchange exchange, GatewayFilterChain chain)方法,在該方法中,先記錄了請求的開始時間,并保存在ServerWebExchange中,此處是一個“pre”類型的過濾器。然后再chain.filter()的內(nèi)部類中的run()方法中相當(dāng)于"post"過濾器,在此處打印了請求所消耗的時間。
接下來將該過濾器注冊到router中,代碼如下。
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/user/**")
.filters(f -> f.filter(new RequestTimeFilter())
.addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
.uri("http://localhost:8504/user/info")
.order(0)
.id("customer_filter_router")
)
.build();
}除了上述代碼的方式配置我們自定義的過濾器的方式之外,也可以在application.yml文件中直接配置,這里不再贅述。
啟動程序,通過curl http://localhost:8098/user/info控制臺會打印出請求消耗時間,日志如下:
....
2019-05-22 15:13:31.221 INFO 19780 --- [ctor-http-nio-4] o.s.cloud.gateway.filter.GatewayFilter : 請求路徑:/user/info消耗時間: 54ms
...
2019-05-22 16:46:23.785 INFO 29928 --- [ctor-http-nio-1] o.s.cloud.gateway.filter.GatewayFilter : 請求路徑:/user/info3消耗時間: 5ms
……
Spring Cloud Gateway根據(jù)作用范圍分為GatewayFilter和GlobalFilter,二者區(qū)別如下:
GatewayFilter : 需要通過spring.cloud.routes.filters 配置在具體路由下,只作用在當(dāng)前路由上或通過spring.cloud.default-filters配置在全局,作用在所有路由上。
在上一小節(jié)中定義的是Gatewayfilter,下面實(shí)現(xiàn)的是Globalfilter:
public class TokenFilter implements GlobalFilter, Ordered {
Logger logger= LoggerFactory.getLogger( TokenFilter.class );
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (token == null || token.isEmpty()) {
logger.info( "token 為空,無法進(jìn)行訪問." );
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}上述代碼實(shí)現(xiàn)了Globalfilter,具體邏輯是判斷請求中是否含參數(shù)token,如果沒有,則校驗(yàn)不通過,對所有請求都有效。如果含有token則轉(zhuǎn)發(fā)到具體后端服務(wù)上,如果沒有則校驗(yàn)不通過。
通過curl http://localhost:8098/user/info進(jìn)行訪問,因?yàn)槁窂街胁缓袇?shù)token,則無法通過校驗(yàn),打印日志如下:
2019-05-22 15:27:11.078 INFO 5956 --- [ctor-http-nio-1] com.song.gateway.TokenFilter : token 為空,無法進(jìn)行訪問.
...
通過curl http://localhost:8098/user/info?token=123進(jìn)行訪問時,則可以獲取到后端服務(wù)返回結(jié)果。
本文由博云研究院原創(chuàng)發(fā)表,轉(zhuǎn)載請注明出處。
文章標(biāo)題:微服務(wù)網(wǎng)關(guān)實(shí)戰(zhàn)——SpringCloudGateway
URL分享:http://www.chinadenli.net/article10/gpdcdo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、面包屑導(dǎo)航、搜索引擎優(yōu)化、網(wǎng)站建設(shè)、品牌網(wǎng)站制作、網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)