吃什么補腎虛效果最好食物焦作整站優(yōu)化
Spring Cloud Gateway路由
文章目錄
- 1. 前言
- 2. Gateway路由的基本概念
- 3. 三種路由
- 1. 靜態(tài)路由
- 2. 動態(tài)路由
- 1. 利用外部存儲
- 2. API動態(tài)路由
- 3. 服務發(fā)現(xiàn)路由(自動路由)
- 3.1. 配置方式
- 3.2 自動路由(服務發(fā)現(xiàn))原理
- 核心源碼
- GatewayDiscoveryClientAutoConfiguration
- `DiscoveryClientRouteDefinitionLocator`核心方法`getRouteDefinitions`
- 4. Gateway路由的核心組件
- 4.1 路由斷言
- 組合并生效相當于并且的關系
- 如果要或的關系需要分配多個路由
- 4.2 路由過濾器
- 路由過濾器概念
- 路由過濾器工廠
- 內置路由過濾器
- 自定義路由過濾器
- 5. 路由的動態(tài)刷新
- 5.1. 添加依賴
- 5.2. 配置application.yml
- 5.3. 創(chuàng)建Config Server
- 5.4. 在遠程Git倉庫中添加配置文件
- 5.5. 啟動Gateway服務
- 5.6. 動態(tài)刷新路由信息
- 6. 處理路由失敗和異常
- 6.1 處理請求失敗和異常
- 6.2 配置回退和重試策略
- 7. 高級路由功能
- 8. 參考文檔
1. 前言
其實Spring Cloud Gateway2.x 的官網文檔寫的已經很全面了,如果想要了解,可以訪問 《Spring Cloud Gateway》
Spring Cloud Gateway是一個基于Spring Boot構建的API網關,主要用于微服務架構中。它提供了一種簡單而高效的方式來對請求進行路由、過濾和轉發(fā),從而實現(xiàn)對服務的統(tǒng)一訪問入口和流量管理。Spring Cloud Gateway通過一個高度可配置的路由規(guī)則集,支持各種復雜的請求處理場景。
與傳統(tǒng)的API網關(如Zuul)相比,Spring Cloud Gateway提供了更好的性能和豐富的功能。它基于響應式編程模型(Reactive)和Spring WebFlux框架構建,能夠提供非阻塞的異步I/O處理,并且能夠很好地與其他Spring Cloud組件進行集成。此外,Spring Cloud Gateway還提供了很多現(xiàn)成的功能,如負載均衡、熔斷、限流等,可以幫助開發(fā)者快速搭建一個完善的API網關。
圖片來自Spring Cloud Gateway2 官網 https://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html
2. Gateway路由的基本概念
- 2.1 路由的定義和作用
路由是一個HTTP請求的處理過程。在Spring Cloud Gateway中,每個請求都會經過一系列的過濾器,最后轉發(fā)到目標URI。
路由配置由三部分組成:ID、目標URI、一系列的斷言和過濾器。ID是路由的唯一標識,目標URI是請求的最終目的地,斷言是用來匹配HTTP請求的規(guī)則,過濾器是用來處理HTTP請求的組件。
在Spring Cloud Gateway中配置路由有兩種方式:通過配置文件和通過代碼。
- 通過配置文件配置路由:
在application.yml文件中添加如下配置:
spring:cloud:gateway:routes:- id: user_routeuri: http://localhost:8080predicates:- Path=/user/**
以上面的配置為例,id為user_route的路由會匹配所有路徑為/user/的請求,然后將其轉發(fā)到http://localhost:8080。
- 通過代碼配置路由:
在配置類中添加如下配置:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {return builder.routes().route("user_route", r -> r.path("/user/**").uri("http://localhost:8080")).build();
}
以上面的配置為例,id為user_route的路由會匹配所有路徑為/user/的請求,然后將其轉發(fā)到http://localhost:8080。
- 2.2 如何在Spring Cloud Gateway中配置路由
3. 三種路由
根據路由的創(chuàng)建方式和使用場景,可以將路由分類為以下三種:
1. 靜態(tài)路由
在Spring Cloud Gateway啟動時,通過配置文件或Java代碼定義的路由規(guī)則。這些路由規(guī)則在運行時是不可修改的。
可以通過配置文件定義靜態(tài)路由:這段配置會將所有以/user開始的請求轉發(fā)到http://localhost:8080。
spring:cloud:gateway:routes:- id: user_routeuri: http://localhost:8080predicates:- Path=/user/**
Spring Cloud Gateway的靜態(tài)路由實現(xiàn)的原理主要通過Reactive模式下的Netty處理方式,以及Project Reactor中的Flux和Mono模型來處理并發(fā)請求。此外,它還使用了Spring 5的核心Webflux框架進行路由分發(fā)。
具體來說,當請求到來時,Spring Cloud Gateway會根據配置文件中的靜態(tài)路由信息,將請求轉發(fā)到相應的目標地址。
核心的代碼主要集中在RoutePredicateHandlerMapping和FilteringWebHandler類中。
// RoutePredicateHandlerMapping用于處理請求的映射關系
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {public RoutePredicateHandlerMapping(GatewayProperties properties, RouteLocator routeLocator, ProxyExchangeArgumentResolver proxyExchangeArgumentResolver, ServerCodecConfigurer serverCodecConfigurer, Environment environment) {...}...
}// FilteringWebHandler用于處理各種過濾器
public class FilteringWebHandler implements WebHandler {private final List<GlobalFilter> globalFilters;public FilteringWebHandler(List<GlobalFilter> globalFilters) {this.globalFilters = globalFilters;}...
}
實際上,配置靜態(tài)路由的主要工作量在于編寫配置文件和理解路由斷言,Spring Cloud Gateway已經為處理了復雜的路由轉發(fā)和過濾器操作。
具體的路由匹配和轉發(fā)過程則由Spring Cloud Gateway框架自動處理。在處理請求時,Gateway會依次經過負載均衡器、過濾器鏈和路由斷言的處理,并最終將請求轉發(fā)到配置的目標地址。
2. 動態(tài)路由
動態(tài)路由通??梢岳斫鉃閮煞N方式,一種通過外部配置存儲動態(tài)更新網關路由,另一種是通過API 的方式動態(tài)新增修改刪除網關路由。
1. 利用外部存儲
動態(tài)路由與靜態(tài)路由的主要區(qū)別在于,動態(tài)路由允許在運行時更新路由配置,而不需要重啟應用。Spring Cloud Gateway
支持通過配置中心(如Spring Cloud Config
)。
實現(xiàn)動態(tài)路由的原理主要是通過監(jiān)聽配置中心的變化,當配置發(fā)生變化時,使用Event機制觸發(fā)路由信息的更新。Spring Cloud Gateway會自動處理新的路由配置并更新其內部的路由表。
核心代碼主要集中在以下幾個類中:
當配置中心發(fā)生更改時,RouteRefreshListener會監(jiān)聽到相關事件,觸發(fā)RefreshRoutesEvent事件,從而使CachingRouteLocator更新路由信息。這樣,Spring Cloud Gateway就可以在運行時動態(tài)地處理新的路由配置。
CachingRouteLocator
:它負責緩存和管理路由信息,同時也會處理路由信息的更新。
public abstract class CachingRouteLocator implements RouteLocator, ApplicationListener<RefreshRoutesEvent> {public abstract Flux<Route> fetch();@Overridepublic void onApplicationEvent(RefreshRoutesEvent event) {// 當收到一個RefreshRoutesEvent事件時,將會觸發(fā)路由信息的更新this.resetRoutes().subscribe();}...
}
RouteDefinitionRouteLocator
:它繼承自CachingRouteLocator
,并負責從RouteDefinitionLocator
中獲取路由定義,然后根據路由定義創(chuàng)建路由。
public class RouteDefinitionRouteLocator extends CachingRouteLocator {public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> routePredicates, RouteDefinitionHandlerFilter routeDefinitionHandlerFilter, ConfigurationService configurationService, GatewayProperties properties) {...}...
}
- RouteRefreshListener:它負責監(jiān)聽配置中心的變化,并在檢測到更改時發(fā)布一個RefreshRoutesEvent事件。
@Configuration
public class RouteRefreshListener {@Beanpublic ApplicationListener<?> routeChangeListener(RouteDefinitionRouteLocator routeDefinitionRouteLocator) {return new ApplicationListener<RefreshRoutesResultEvent>() {@Overridepublic void onApplicationEvent(RefreshRoutesResultEvent event) {routeDefinitionRouteLocator.onApplicationEvent(new RefreshRoutesEvent(this));}};}
}
要實現(xiàn)動態(tài)路由,還需要在配置文件中配置相應的配置中心,例如使用Spring Cloud Config:
spring:cloud:gateway:discovery:locator:enabled: trueroutes:- id: dynamic_routeuri: lb://service-idpredicates:- Path=/dynamic_path/**config:uri: http://config-server-urilabel: mastername: gateway
2. API動態(tài)路由
在Spring Cloud Gateway運行時,通過調用API動態(tài)創(chuàng)建和修改的路由規(guī)則。這種路由規(guī)則可以根據需要進行實時的修改。動態(tài)路由通常需要通過編程方式創(chuàng)建。
在Spring Cloud Gateway中,可以通過調用Gateway API來動態(tài)地創(chuàng)建和修改路由規(guī)則。這些路由規(guī)則可以在運行時進行實時修改。要實現(xiàn)這一功能,需要使用RouteDefinitionWriter
接口,它提供了添加、刪除和更新路由定義的方法。
Spring Cloud Gateway中動態(tài)地創(chuàng)建和修改路由規(guī)則
依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
創(chuàng)建一個RouteController
來處理動態(tài)路由的創(chuàng)建、更新和刪除操作
@RestController
@RequestMapping("/route")
public class RouteController {@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;@Autowiredprivate RouteDefinitionLocator routeDefinitionLocator;@PostMappingpublic Mono<Void> add(@RequestBody RouteDefinition routeDefinition) {return routeDefinitionWriter.save(Mono.just(routeDefinition)).then();}@PutMappingpublic Mono<Void> update(@RequestBody RouteDefinition routeDefinition) {return routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())).then(routeDefinitionWriter.save(Mono.just(routeDefinition))).then();}@DeleteMapping("/{id}")public Mono<Void> delete(@PathVariable String id) {return routeDefinitionWriter.delete(Mono.just(id));}@GetMappingpublic Flux<RouteDefinition> getRoutes() {return routeDefinitionLocator.getRouteDefinitions();}
}
在這個例子中,使用RouteDefinitionWriter
保存、更新和刪除路由定義。當定義新的路由規(guī)則或者更新現(xiàn)有的路由規(guī)則時,會自動觸發(fā)路由表的刷新。
現(xiàn)在就可以使用HTTP API來動態(tài)地創(chuàng)建、修改和刪除路由規(guī)則了:
-
添加一個新的路由規(guī)則:
POST /route {"id": "my_route","uri": "http://example.com","predicates": [{"name": "Path","args": {"pattern": "/mypath/**"}}],"filters": [{"name": "RewritePath","args": {"regexp": "/mypath/(?<segment>.*)","replacement": "/${segment}"}}] }
-
更新現(xiàn)有的路由規(guī)則:
PUT /route {"id": "my_route","uri": "http://example.com","predicates": [{"name": "Path","args": {"pattern": "/mypath_v2/**"}}],"filters": [{"name": "RewritePath","args": {"regexp": "/mypath_v2/(?<segment>.*)","replacement": "/${segment}"}}] }
-
刪除現(xiàn)有的路由規(guī)則:
DELETE /route/my_route
通過這種方法,可以在Spring Cloud Gateway運行時動態(tài)地創(chuàng)建和修改路由規(guī)則,以滿足不斷變化的業(yè)務需求。
3. 服務發(fā)現(xiàn)路由(自動路由)
3.1. 配置方式
這是一種特殊的動態(tài)路由,其路由規(guī)則是根據服務發(fā)現(xiàn)機制來自動創(chuàng)建的。
在微服務架構中,服務發(fā)現(xiàn)是一種特別重要的機制,它讓微服務能夠自動地發(fā)現(xiàn)網絡中的其他服務,并知道如何與它們進行交互。Spring Cloud Gateway通過與服務注冊中心(如Eureka、Consul等)集成,實現(xiàn)了自動的服務發(fā)現(xiàn)路由功能。
當一個新的服務實例被注冊到服務注冊中心時,Spring Cloud Gateway會自動發(fā)現(xiàn)它,并創(chuàng)建一個新的路由規(guī)則,將請求轉發(fā)到這個服務。同樣,當一個服務實例下線或被注銷時,對應的路由規(guī)則也會被自動刪除,這就是所謂的服務發(fā)現(xiàn)路由或者自動路由。
這種機制可以極大地簡化服務間的交互,因為不需要手動地為每一個服務定義路由規(guī)則。
在Spring Cloud Gateway中使用服務發(fā)現(xiàn)的一個簡單示例
添加 Spring Cloud Gateway和 Eureka的依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
application.yml
文件中配置Eureka和Gateway:
spring:cloud:gateway:discovery:locator:enabled: true # 啟用服務發(fā)現(xiàn)路由application:name: gateway-service
eureka:client:service-url:defaultZone: http://localhost:8761/eureka/
啟動Eureka Server和的服務,然后啟動Spring Cloud Gateway,Gateway就會自動發(fā)現(xiàn)注冊在Eureka中的服務,并為它們創(chuàng)建路由規(guī)則。例如有一個名為user-service
的服務,那么就可以通過http://localhost:8080/user-service
來訪問。
3.2 自動路由(服務發(fā)現(xiàn))原理
在Spring Cloud Gateway中實現(xiàn)服務發(fā)現(xiàn)路由的核心類是DiscoveryClientRouteDefinitionLocator
。這個類負責從注冊中心獲取服務實例信息,并基于這些信息創(chuàng)建路由定義。當服務實例發(fā)生變化時,DiscoveryClientRouteDefinitionLocator
會自動更新路由定義。
服務發(fā)現(xiàn)路由的底層原理主要包括以下幾個方面:
-
集成服務發(fā)現(xiàn)組件:Spring Cloud Gateway可以與多種服務注冊中心(如Eureka、Consul等)集成。這些集成是通過實現(xiàn)
DiscoveryClient
接口來完成的。DiscoveryClient
提供了獲取服務實例信息的方法,例如getInstances(String serviceId)
。這使得Gateway可以獲取到注冊中心中所有服務的實例信息。 -
創(chuàng)建路由定義:
DiscoveryClientRouteDefinitionLocator
會將從DiscoveryClient
獲取到的服務實例信息轉換為RouteDefinition
對象。這些RouteDefinition
對象包含了路由的基本信息,如ID、URI、斷言和過濾器等。通過這些信息,Gateway可以知道如何將請求路由到特定的服務實例。 -
路由規(guī)則更新:當注冊中心中的服務實例發(fā)生變化時,
DiscoveryClientRouteDefinitionLocator
會自動更新路由規(guī)則。這是通過監(jiān)聽服務實例變化事件來實現(xiàn)的。當接收到服務實例變化事件后,DiscoveryClientRouteDefinitionLocator
會重新獲取服務實例信息,并更新RouteDefinition
對象。這樣,Gateway就能實時地感知到服務實例的變化,并相應地調整路由規(guī)則。 -
請求轉發(fā):當Gateway接收到一個請求時,它會根據
DiscoveryClientRouteDefinitionLocator
提供的路由定義匹配相應的路由規(guī)則。然后,Gateway會將請求轉發(fā)到匹配的服務實例。這個轉發(fā)過程是通過NettyRoutingFilter
完成的,它會根據RouteDefinition
中的URI信息轉發(fā)請求。
核心源碼
GatewayDiscoveryClientAutoConfiguration
我們可以看到 當 DiscoveryClient
存在并且 spring.cloud.gateway.discovery.locator.enabled
為 true
時,創(chuàng)建一個 DiscoveryClientRouteDefinitionLocator
Bean,允許 Spring Cloud Gateway 動態(tài)地從服務注冊中心發(fā)現(xiàn)路由定義。
DiscoveryClientRouteDefinitionLocator
核心方法getRouteDefinitions
getRouteDefinitions
方法的主要目的是從 DiscoveryClient
獲取服務實例信息并將其轉換為路由定義。首先解析 includeExpression
和 urlExpression
,然后根據 includeExpression
創(chuàng)建一個用于判斷服務實例是否需要被包含在路由定義中的 Predicate
。接著從 DiscoveryClient
獲取所有服務實例信息,并轉換為 RouteDefinition
。轉換過程中會生成 PredicateDefinition
和 FilterDefinition
。最后返回一個包含所有路由定義的 Flux
對象。
@Override
public Flux<RouteDefinition> getRouteDefinitions() {// 創(chuàng)建一個 SpelExpressionParser 用于解析表達式SpelExpressionParser parser = new SpelExpressionParser();// 解析 includeExpression,用于判斷服務實例是否需要被包含在路由定義中Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());// 解析 urlExpression,用于生成服務實例對應的 URIExpression urlExpr = parser.parseExpression(properties.getUrlExpression());// 根據 includeExpression 創(chuàng)建一個 Predicate,用于判斷服務實例是否需要被包含在路由定義中Predicate<ServiceInstance> includePredicate;if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {includePredicate = instance -> true;} else {includePredicate = instance -> {Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);if (include == null) {return false;}return include;};}// 從 DiscoveryClient 獲取所有服務實例信息并轉換為路由定義return Flux.fromIterable(discoveryClient.getServices()).map(discoveryClient::getInstances) // 獲取每個服務的所有實例.filter(instances -> !instances.isEmpty()) // 過濾掉沒有實例的服務.map(instances -> instances.get(0)) // 獲取每個服務的第一個實例.filter(includePredicate) // 過濾掉不需要被包含在路由定義中的服務實例.map(instance -> { // 將服務實例轉換為 RouteDefinitionString serviceId = instance.getServiceId();RouteDefinition routeDefinition = new RouteDefinition();routeDefinition.setId(this.routeIdPrefix + serviceId);// 獲取服務實例對應的 URIString uri = urlExpr.getValue(evalCtxt, instance, String.class);routeDefinition.setUri(URI.create(uri));final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);// 生成 PredicateDefinitionfor (PredicateDefinition original : this.properties.getPredicates()) {PredicateDefinition predicate = new PredicateDefinition();predicate.setName(original.getName());for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);predicate.addArg(entry.getKey(), value);}routeDefinition.getPredicates().add(predicate);}// 生成 FilterDefinitionfor (FilterDefinition original : this.properties.getFilters()) {FilterDefinition filter = new FilterDefinition();filter.setName(original.getName());for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);filter.addArg(entry.getKey(), value);}routeDefinition.getFilters().add(filter);}return routeDefinition;});
}
4. Gateway路由的核心組件
4.1 路由斷言
路由斷言(Route Predicates)是Spring Cloud Gateway中的一個核心概念,它負責匹配HTTP請求的屬性,并決定是否將請求路由到特定的服務。
使用斷言,可以基于許多不同的請求屬性定義路由規(guī)則,包括:
- 請求路徑:比如請求的URL。
- 請求方法:GET、POST等。
- 請求頭:可以匹配特定的請求頭字段和值。
- 查詢參數(shù):可以匹配URL的查詢參數(shù)。
- Cookie:可以匹配特定的cookie。
斷言的定義通常在Spring配置文件(如application.yaml)中,作為路由定義的一部分。每個斷言都是一種謂詞(Predicate),這是一個返回true或false的函數(shù)。
路由斷言的示例:
spring:cloud:gateway:routes:- id: user_serviceuri: http://localhost:8080/userpredicates:- Path=/user/**- Method=GET- Header=X-Request-User, \d+
在這個例子中,定義了一個名為"user_service"的路由,其目標URI為"http://localhost:8080/user"。定義了三個斷言:
- Path斷言:只有當請求路徑以"/user/"開頭時,才會觸發(fā)該路由。
- Method斷言:只有當HTTP請求方法為GET時,才會觸發(fā)該路由。
- Header斷言:只有當請求頭中存在名為"X-Request-User"的字段,并且其值為數(shù)字時,才會觸發(fā)該路由。
只有當所有的斷言都返回true時,該請求才會被路由到目標服務。否則,Spring Cloud Gateway將返回404錯誤。
組合并生效相當于并且的關系
在這個示例中,只有滿足以下所有條件的請求才會路由到http://localhost:8080/:
- 請求路徑以/api/開頭
- 請求方法為GET
- 請求頭中包含X-Request-Id,且其值為數(shù)字
- 查詢參數(shù)中包含debug,并且其值為true
- Cookie中包含sessionId,并且其值不為空
spring:cloud:gateway:routes:- id: host_routeuri: http://localhost:8080/predicates:- Path=/api/**- Method=GET- Header=X-Request-Id, \d+- Query=debug, true- Cookie=sessionId, .+
如果要或的關系需要分配多個路由
如果想要以"或"的關系生效,需要定義多個路由??紤]到Spring Cloud Gateway的工作方式,一個請求只能被一個路由處理。這意味著謂詞是以"與"的方式工作的,而不是"或"。如果想要一個請求被多個謂詞處理,需要創(chuàng)建多個路由。每個路由有一個謂詞,這樣一個請求可以匹配多個路由。
在以下的配置中,如果請求路徑以/api/開頭, 或請求方法為GET, 或請求頭中包含X-Request-Id且其值為數(shù)字, 或查詢參數(shù)中包含debug且其值為true, 或Cookie中包含sessionId且其值不為空,任何一個條件滿足,請求都會路由到http://localhost:8080/。
spring:cloud:gateway:routes:- id: host_route1uri: http://localhost:8080/predicates:- Path=/api/**- id: host_route2uri: http://localhost:8080/predicates:- Method=GET- id: host_route3uri: http://localhost:8080/predicates:- Header=X-Request-Id, \d+- id: host_route4uri: http://localhost:8080/predicates:- Query=debug, true- id: host_route5uri: http://localhost:8080/predicates:- Cookie=sessionId, .+
4.2 路由過濾器
Spring Cloud Gateway作為一個API網關,提供了很多功能,如路由轉發(fā)、斷路器、限流等。其中,路由過濾器是其核心功能之一,它允許我們在請求被路由之前或之后對請求進行處理。本文將詳細介紹Spring Cloud Gateway的路由過濾器。
路由過濾器概念
路由過濾器是一個Java類,用于修改進入或退出網關的HTTP請求和響應。它包含兩種類型的過濾器:
- Global Filter:全局過濾器,對所有路由請求生效。
- GatewayFilter:網關過濾器,只對特定路由請求生效。
Spring Cloud Gateway中的過濾器基于WebFilter
接口實現(xiàn),并且實現(xiàn)了Ordered
接口來控制過濾器的執(zhí)行順序。過濾器可以在請求被路由之前(Pre過濾器)或之后(Post過濾器)進行處理。
路由過濾器工廠
Spring Cloud Gateway的過濾器是通過工廠創(chuàng)建的。這些工廠實現(xiàn)了GatewayFilterFactory
接口,這個接口包含兩個方法:
apply(T config)
:將配置信息傳遞給過濾器,并創(chuàng)建過濾器實例。getConfigClass()
:返回過濾器所使用的配置類。
內置路由過濾器
Spring Cloud Gateway提供了許多內置過濾器,可以覆蓋大部分基本功能。以下是一些常用的內置過濾器:
- AddRequestHeader:添加請求頭。
- AddRequestParameter:添加請求參數(shù)。
- AddResponseHeader:添加響應頭。
- PrefixPath:添加前綴路徑。
- RewritePath:重寫請求路徑。
- Retry:重試。
- SetPath:設置請求路徑。
- SetResponseHeader:設置響應頭。
- StripPrefix:去掉前綴。
- Hystrix:使用Hystrix斷路器。
自定義路由過濾器
除了內置過濾器以外,我們還可以自定義過濾器。自定義過濾器需要實現(xiàn)GatewayFilter
接口,并重寫filter(ServerWebExchange exchange, GatewayFilterChain chain)
方法。下面是一個簡單的自定義過濾器示例:
創(chuàng)建了一個名為 CustomGatewayFilterFactory
的類,實現(xiàn)了 GatewayFilterFactory<GatewayFilterFactory.Args>
接口,并重寫了 apply
和 name
方法。在 apply
方法中,我們返回一個新的 CustomGatewayFilter
實例。在 name
方法中,我們返回過濾器的名稱,這個名稱需要與配置文件中的 filters
部分所引用的名稱相匹配。最后,我們將 CustomGatewayFilterFactory
注冊為一個 Bean。
public class CustomGatewayFilter implements GatewayFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 在請求被路由之前執(zhí)行的邏輯System.out.println("Custom Gateway Filter Pre");return chain.filter(exchange).then(Mono.fromRunnable(() -> {// 在請求被路由之后執(zhí)行的邏輯System.out.println("Custom Gateway Filter Post");}));}
}
要在配置文件中為特定路由配置 CustomGatewayFilter
,需要在 Spring Cloud Gateway 的配置文件(如 application.yml
或 application.properties
)中定義路由規(guī)則,并在 filters
部分引用的 CustomGatewayFilter
。以下是一個在 application.yml
中配置 CustomGatewayFilter
的示例:
spring:cloud:gateway:routes:- id: my_routeuri: http://example.com # 目標服務的地址predicates:- Path=/my_path/** # 路由條件,例如路徑匹配filters:- name: CustomFilter # 在這里引用的自定義過濾器
需要先將 CustomGatewayFilter
注冊為一個全局過濾器。為了達到這個目的,需要創(chuàng)建一個 GatewayFilterFactory
,并將其注冊為一個 Bean。請參考以下示例:
@Configuration
public class CustomGatewayFilterConfiguration {@Beanpublic CustomGatewayFilterFactory customGatewayFilterFactory() {return new CustomGatewayFilterFactory();}public static class CustomGatewayFilterFactory implements GatewayFilterFactory<GatewayFilterFactory.Args> {@Overridepublic GatewayFilter apply(GatewayFilterFactory.Args args) {return new CustomGatewayFilter();}@Overridepublic String name() {return "CustomFilter";}}
}
5. 路由的動態(tài)刷新
我們寫一個簡化的教程來看下如何在Spring Cloud Gateway中實現(xiàn)動態(tài)路由刷新。
5.1. 添加依賴
在Gateway服務的pom.xml
文件中添加以下依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
這里我們使用RabbitMQ作為消息代理,如果想使用其他消息代理,請?zhí)鎿Q相應的依賴。
5.2. 配置application.yml
在Gateway服務的src/main/resources/application.yml
中添加以下配置:
spring:cloud:gateway:discovery:locator:enabled: trueroutes:- id: user-serviceuri: lb://user-servicepredicates:- Path=/user-service/**config:uri: http://localhost:8888rabbitmq:host: localhostport: 5672username: guestpassword: guestbus:id: ${spring.application.name}:${server.port}
這里我們配置了一個名為user-service
的路由,路由的配置信息將從Config Server(地址為http://localhost:8888
)獲取。
5.3. 創(chuàng)建Config Server
創(chuàng)建一個新的Spring Boot項目,名為config-server
。在pom.xml
文件中添加以下依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId>
</dependency>
在src/main/resources/application.yml
中添加以下配置:
server:port: 8888
spring:cloud:config:server:git:uri: https://github.com/your-username/your-repo.git
這里配置了Config Server從遠程Git倉庫獲取配置信息。
在src/main/java/com/example/configserver/ConfigServerApplication.java
中添加@EnableConfigServer
注解以啟動Config Server:
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {public static void main(String[] args) {SpringApplication.run(ConfigServerApplication.class, args);}
}
啟動Config Server。
5.4. 在遠程Git倉庫中添加配置文件
在的遠程Git倉庫中添加一個名為gateway-service.yml
的文件,內容如下:
spring:cloud:gateway:routes:- id: user-serviceuri: lb://user-servicepredicates:- Path=/user-service/**
這里配置了一個名為user-service
的路由。
5.5. 啟動Gateway服務
現(xiàn)在啟動的Gateway服務,它將從Config Server獲取最新的路由信息??梢酝ㄟ^訪問http://localhost:8080/user-service/some-api
來測試路由是否生效。
5.6. 動態(tài)刷新路由信息
當gateway-service.yml
文件發(fā)生變化并推送到遠程Git倉庫后,可以發(fā)送一個POST請求到http://localhost:8080/actuator/refresh
端點以刷新Gateway的路由信息。
也可以在config-server
項目中添加Spring Cloud Bus的依賴,并添加相應的配置,以實現(xiàn)自動廣播路由變更消息。
6. 處理路由失敗和異常
如何處理路由失敗和異常也是大家在寫一個高可用,健壯性良好的網關服務的基本要求。那么在本教程中,我們來了解一下Spring Cloud Gateway中如何處理路由失敗和異常,以及如何配置回退和重試策略。
6.1 處理請求失敗和異常
當路由轉發(fā)過程中發(fā)生異常,例如目標服務不可用,可以使用Gateway的全局異常處理器來捕獲異常并返回一個友好的錯誤響應。默認情況下,Gateway會使用org.springframework.cloud.gateway.handler.GlobalErrorWebExceptionHandler
作為全局異常處理器。可以通過實現(xiàn)ErrorWebExceptionHandler
接口并注冊為Spring Bean來自定義全局異常處理器。
寫一個簡單的例子可供參考
自定義CustomGlobalExceptionHandler繼承了AbstractErrorWebExceptionHandler
并覆蓋了getRoutingFunction
方法,以自定義錯誤響應。
@Component
public class CustomGlobalExceptionHandler extends AbstractErrorWebExceptionHandler {public CustomGlobalExceptionHandler(ErrorAttributes errorAttributes,WebProperties.Resources resources,ApplicationContext applicationContext,ServerCodecConfigurer serverCodecConfigurer) {super(errorAttributes, resources, applicationContext);setMessageWriters(serverCodecConfigurer.getWriters());setMessageReaders(serverCodecConfigurer.getReaders());}@Overrideprotected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);}private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {Throwable error = getError(request);// 自定義響應體Map<String, Object> errorAttributes = new HashMap<>();errorAttributes.put("message", error.getMessage());errorAttributes.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());errorAttributes.put("timestamp", LocalDateTime.now());return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(errorAttributes));}
}
默認實現(xiàn)是DefaultErrorWebExceptionHandler
6.2 配置回退和重試策略
使用Spring Cloud Gateway,可以為路由配置回退和重試策略?;赝瞬呗栽试S在轉發(fā)請求失敗時返回一個預定義的響應。重試策略允許在請求失敗時嘗試重新發(fā)送請求。
為了配置回退策略,可以在路由配置中添加fallbackUri
屬性。
spring:cloud:gateway:routes:- id: user-serviceuri: lb://user-servicepredicates:- Path=/user-service/**filters:- name: Hystrixargs:name: user-servicefallbackUri: forward:/fallback
為user-service
路由配置了一個回退URI,它會將請求轉發(fā)到/fallback
端點。需要在的Gateway服務中實現(xiàn)這個端點,并返回一個預定義的響應。
為了配置重試策略,可以在路由配置中添加Retry
過濾器。
為user-service
路由配置了一個重試策略。當請求方法為GET
且服務器返回500 Internal Server Error
狀態(tài)時,Gateway會嘗試重新發(fā)送請求,最多重試3次。
spring:cloud:gateway:routes:- id: user-serviceuri: lb://user-servicepredicates:- Path=/user-service/**filters:- name: Retryargs:retries: 3statuses: INTERNAL_SERVER_ERRORmethods: GET
7. 高級路由功能
寫不動了,路由詳解就到這兒,后面有時間補充一下高級路由功能。
- 7.1 如何配置負載均衡
- 7.2 如何配置熔斷器
- 7.3 如何配置速率限制
8. 參考文檔
Spring Cloud Gateway官方文檔 https://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html