Spring Cloud Gateway is built upon Spring Boot 2.0, Spring WebFlux, and Project Reactor. As a consequence many of the familiar synchronous libraries (Spring Data and Spring Security, for example) and patterns you may not apply when using Spring Cloud Gateway. If you are unfamiliar with these projects we suggest you begin by reading their documentation to familiarize yourself with some of the new concepts before working with Spring Cloud Gateway.html
必需要補 Spring WebFlux 和 Project Reactor 的技術java
Spring Cloud Gateway requires the Netty runtime provided by Spring Boot and Spring Webflux. It does not work in a traditional Servlet Container or built as a WAR.react
Netty 也要補web
依賴:正則表達式
以前學的東西都直接用進來,去服務發現上註冊,從集中配置上拉配置文件redis
<!-- 網關依賴 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
配置(bootstrap.yml):spring
server: port: 9001 spring: application: name: gateway-server profiles: active: dev cloud: config: label: master profile: ${spring.profiles.active} discovery: service-id: config-server enabled: true eureka: client: service-url: defaultZone: http://127.0.0.1:8761/eureka/
啓動類:bootstrap
@SpringCloudApplication @RestController public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } @Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { return builder.routes() .route(p -> p .path("/get") .filters(f -> f.addRequestHeader("Hello", "World")) .uri("http://localhost:4001/config/param")) .build(); } }
Route:路由網關的基本構建塊。它由ID,目標URI,謂詞集合和過濾器集合定義。若是聚合謂詞爲真,則匹配路由緩存
Predicate:這是一個Java 8函數斷言。輸入類型是Spring Framework ServerWebExchange
。這容許開發人員匹配來自HTTP請求的任何內容,例如標頭或參數。cookie
Filters:這些是使用特定工廠構建的Spring Framework GatewayFilter
實例。這裏,能夠在發送下游請求以前或以後修改請求和響應。
工做流程:
客戶端向Spring Cloud Gateway發出請求。若是網關處理程序映射肯定請求與路由匹配,則將其發送到網關Web處理程序。此處理程序運行經過特定於請求的過濾器鏈發送請求。濾波器被虛線劃分的緣由是濾波器能夠在發送代理請求以前或以後執行邏輯。執行全部「預」過濾器邏輯,而後進行代理請求。在發出代理請求以後,執行「post」過濾器邏輯。
在沒有端口的路由中定義的URI將分別爲HTTP和HTTPS URI獲取默認端口設置爲80和443。
Spring Cloud Gateway將路由做爲Spring WebFlux
HandlerMapping
基礎結構的一部分進行匹配。Spring Cloud Gateway包含許多內置的Route Predicate工廠。全部這些 Predicate 都匹配HTTP請求的不一樣屬性。多路線謂詞工廠能夠組合並經過邏輯組合and
。
Predicate來自於java8的接口。Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將Predicate組合成其餘複雜的邏輯(好比:與,或,非)。能夠用於接口請求參數校驗、判斷新老數據是否有變化須要進行更新操做。add–與、or–或、negate–非。
配置:
spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver]
內置的Predicate
一、請求時間匹配
二、Cookie匹配
chocolate
who的值與ch.p
正則表達式匹配的cookie三、Header匹配
X-Request-Id
其值與\d+
正則表達式匹配(具備一個或多個數字的值),則此路由匹配四、Host匹配
Host
header has the value www.somehost.org
or beta.somehost.org
or www.anotherhost.org
.sub
如上例中定義的)提取爲名稱和值的映射,並將其放在ServerWebExchange.getAttributes()
帶有定義的鍵的位置ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
五、Method匹配
六、Path匹配
/foo/1
or /foo/bar
or /bar/baz
.segment
如上例中定義的)提取爲名稱和值的映射,並將其放在ServerWebExchange.getAttributes()
帶有定義的鍵的位置ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
七、QueryParam匹配
baz
query parameter.foo
其值與ba.
regexp 匹配的查詢參數,則此路由將匹配,所以bar
而且baz
將匹配八、Remote匹配
192.168.1.10
.****示例:
一、時間匹配
@Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { ZonedDateTime zonedDateTime = ZonedDateTime.now().plusDays(-1); String url = "http://localhost:4001/config/param"; return builder.routes() .route("before-predicate", predicateSpec -> predicateSpec .before(zonedDateTime) .uri(url)) .build(); }
二、Cookie匹配
@Bean public RouteLocator routers(RouteLocatorBuilder builder) { String url = "http://localhost:4001/config/param"; return builder.routes() .route(predicateSpec -> predicateSpec .cookie("cookieName", "^\\d+$") .uri(url)) .build(); }
三、Host匹配
@Bean public RouteLocator routers(RouteLocatorBuilder builder) { String url = "http://localhost:4001/config/param"; return builder.routes() .route(predicateSpec -> predicateSpec.host("localhost").uri(url)) .build(); }
4 ...
一、按執行順序分
pre過濾器:轉發以前執行。參數校驗、權限校驗、流量監控、日誌輸出、協議轉換等
post過濾器:轉發以後執行。響應內容、響應頭的修改,日誌的輸出,流量監控等
二、按做用範圍分
global filter:全部路由
gateway filter:指定的路由
一、AddRequestHeaderGatewayFilterFactory:在匹配的請求上添加請求頭,如示例中,添加請求頭:X-Request-Foo;值:bar
spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org filters: # 添加請求頭 X-Request-Foo:Bar - AddRequestParameter=X-Request-Foo, Bar
二、AddRequestParameterGatewayFilterFactory:在匹配的請求上添加請求參數
spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org filters: # 添加請求參數 foo=bar; 在query串中添加 - AddRequestParameter=foo, bar
三、AddResponseHeaderGatewayFilterFactory:在匹配的請求上添加響應頭
spring: cloud: gateway: routes: - id: add_response_header_route uri: https://example.org filters: # 添加響應頭 X-Response-Foo:Bar - AddResponseHeader=X-Response-Foo, Bar
四、HystrixGatewayFilterFactory:將斷路器引入網關路由
spring: cloud: gateway: routes: - id: hystrix_route uri: https://example.org filters: - Hystrix=myCommandName
五、HystrixGatewayFilterFactory:將斷路器引入網關路由,保護您的服務免受級聯故障的影響,並容許您在下游故障時提供回退響應。
六、PrefixPathGatewayFilterFactory:將爲/mypath
全部匹配請求的路徑添加前綴
七、PreserveHostHeaderGatewayFilterFactory:設置路由過濾器將檢查的請求屬性,以肯定是否應發送原始主機頭,而不是http客戶端肯定的主機頭。
八、RequestRateLimiterGatewayFilterFactory:RateLimiter
實現來肯定是否容許當前請求繼續。若是不是,HTTP 429 - Too Many Requests
則返回(默認狀況下)狀態。
九、RedirectToGatewayFilterFactory: takes a status
and a url
parameter. The status should be a 300 series redirect http code, such as 301. The url should be a valid url. This will be the value of the Location
header
十、RemoveRequestHeaderGatewayFilterFactory:接受一個name
參數。它是要刪除的標頭的名稱
十一、RemoveResponseHeaderGatewayFilterFactory:接受一個name
參數。它是要刪除的標頭的名稱
十二、...
全部實例
GlobalFilter
和全部路由特定實例添加GatewayFilter
到過濾器鏈中。這個組合的過濾器鏈按org.springframework.core.Ordered
接口排序,能夠經過實現getOrder()
方法或使用@Order
註釋來設置。
一、LoadBalancerClientFilter:交換屬性查找一個URI ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
。若是url有一個lb
方案(即lb://myservice
),它將使用Spring Cloud LoadBalancerClient
將名稱(myservice
在前面的示例中)解析爲實際的主機和端口,並替換相同屬性中的URI。未修改的原始URL將附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR
屬性中的列表中。過濾器還將查看ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR
屬性以查看它是否等於lb
,而後應用相同的規則。
二、...
The Gateway can be configured to create routes based on services registered with a DiscoveryClient
compatible service registry.
一、set spring.cloud.gateway.discovery.locator.enabled=true
and make sure a DiscoveryClient
implementation is on the classpath and enabled (such as Netflix Eureka, Consul or Zookeeper).
二、Configuring Predicates and Filters For DiscoveryClient Routes
三、自定義DiscoveryClient
路由使用的謂詞和/或過濾器,能夠經過設置spring.cloud.gateway.discovery.locator.predicates[x]
和來自定義spring.cloud.gateway.discovery.locator.filters[y]
。
spring.cloud.gateway.discovery.locator.predicates[0].name:Path spring.cloud.gateway.discovery.locator.predicates[0].args [pattern]:「'/'+ serviceId +'/ **'」 spring.cloud.gateway.discovery.locator.predicates[1].name:Host spring.cloud.gateway.discovery.locator.predicates[1].args [pattern]:「'**。foo.com'」 spring.cloud.gateway.discovery.locator.filters[0].name:Hystrix spring.cloud.gateway.discovery.locator.filters[0].args [name]:serviceId spring.cloud.gateway.discovery.locator.filters[1].name:RewritePath spring.cloud.gateway.discovery.locator.filters[1].args [regexp]:「'/'+ serviceId +'/(?<remaining>.*)'」 spring.cloud.gateway.discovery.locator.filters[1].args [replacement]:「'/ $ {remaining}'」
監控端點:
GET /actuator/gateway/globalfilters:檢索應用於全部路由的全局過濾器
GET /actuator/gateway/routefilters:查看路由過濾器
POST /actuator/gateway/refresh:刷新路由緩存
GET /actuator/gateway/routes:檢索網關中定義的路由
GET /actuator/gateway/routes/{id}:檢索特定路徑的路由信息
POST/DELETE /gateway/routes/{id_route_to_create}:建立或刪除特定路由
一、初始化路由
不管是yml仍是代碼,這些配置最終都是被封裝到RouteDefinition對象中。
一個RouteDefinition有個惟一的ID,若是不指定,就默認是UUID,多個RouteDefinition組成了gateway的路由系統。
全部路由信息在系統啓動時就被加載裝配好了,並存到了內存裏
涉及到的相關bean
RouteDefinitionRepository: 從存儲器中加載路由
public interface RouteDefinitionLocator { Flux<RouteDefinition> getRouteDefinitions(); }
二、實現本身的加載邏輯,這裏從redis中加載
添加依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency>
建立RedisRouteDefinitionRepository,實現RouteDefinitionRepository
@Component @Slf4j @AllArgsConstructor @SuppressWarnings("all") public class RedisRouteDefinitionRepository implements RouteDefinitionRepository { private static final String ROUTE_KEY = "gateway_route_key"; private final RedisTemplate redisTemplate; @Override public Flux<RouteDefinition> getRouteDefinitions() { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinition.class)); List<RouteDefinition> values = redisTemplate.opsForHash().values(ROUTE_KEY); log.debug("redis 中路由定義條數: {}, {}", values.size(), values); return Flux.fromIterable(values); } @Override public Mono<Void> save(Mono<RouteDefinition> route) { return route.flatMap(r -> { log.info("保存路由信息{}", r); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.opsForHash().put(ROUTE_KEY, r.getId(), r); return Mono.empty(); }); } @Override public Mono<Void> delete(Mono<String> routeId) { routeId.subscribe(id -> { log.info("刪除路由信息{}", id); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.opsForHash().delete(ROUTE_KEY, id); }); return Mono.empty(); } }
路由的定義能夠任何來源初始化到redis,這裏只模擬
@PostConstruct public void main() { RouteDefinition definition = new RouteDefinition(); definition.setId("id"); URI uri = UriComponentsBuilder.fromHttpUrl("http://localhost:4001/config/param").build().toUri(); definition.setUri(uri); //定義第一個斷言 PredicateDefinition predicate = new PredicateDefinition(); predicate.setName("Path"); Map<String, String> predicateParams = new HashMap<>(8); predicateParams.put("pattern", "/get"); predicate.setArgs(predicateParams); //定義Filter FilterDefinition filter = new FilterDefinition(); filter.setName("AddRequestHeader"); Map<String, String> filterParams = new HashMap<>(8); //該_genkey_前綴是固定的,見org.springframework.cloud.gateway.support.NameUtils類 filterParams.put("_genkey_0", "header"); filterParams.put("_genkey_1", "addHeader"); filter.setArgs(filterParams); FilterDefinition filter1 = new FilterDefinition(); filter1.setName("AddRequestParameter"); Map<String, String> filter1Params = new HashMap<>(8); filter1Params.put("_genkey_0", "param"); filter1Params.put("_genkey_1", "addParam"); filter1.setArgs(filter1Params); definition.setFilters(Arrays.asList(filter, filter1)); definition.setPredicates(Arrays.asList(predicate)); System.out.println("definition:" + JSONUtil.toJsonStr(definition)); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinition.class)); redisTemplate.opsForHash().put(ROUTE_KEY, "router1", definition); }
啓動網關,裝配RedisRouteDefinitionRepository後,spring調用main(),建立一條路由
GET /actuator/gateway/routes:查看當前路由信息
三、也可使用http請求實現,這裏就不寫了。