Route(路由)
1、構架pom文件,這裏面需要將web包去掉,因爲Gateway的底層是webflux,而webflux和web是衝突的,只能有一個,需要注意!
<?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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.king.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-gateway9527</artifactId>
<dependencies>
<!--新增gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client 客戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.king.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2、構建main方法入口,因爲gateway也需要註冊進註冊中心,所以也需要添加註解@EnableEurekaClient
package com.king.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* created by king on 2020/4/23 1:19 下午
*/
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class,args);
}
}
3、構建yml文件,此處是簡單的路由配置,等演示成功後,後邊會更改爲動態路由配置
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh1 #路由的ID,沒有固定規則但要求唯一,建議配合服務名
uri: http://localhost:8001 #匹配後提供服務的路由地址
predicates:
- Path=/payment/get/** #斷言,路徑相匹配的進行路由
- id: payment_routh2 #路由的ID,沒有固定規則但要求唯一,建議配合服務名
uri: http://localhost:8001 #匹配後提供服務的路由地址
predicates:
- Path=/payment/lb/** #斷言,路徑相匹配的進行路由
eureka:
client:
#表示是否將自己註冊進EurekaServer默認爲true。
register-with-eureka: true
#是否從EurekaServer抓取已有的註冊信息,默認爲true。單節點無所謂,集羣必須設置爲true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
#單機
defaultZone: http://localhost:7001/eureka
# 集羣
#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: gateway9527
prefer-ip-address: true #訪問路徑可以顯示ip地址
#Eureka客戶端向服務端發送心跳的時間間隔,單位爲秒(默認30s)
#lease-renewal-interval-in-seconds: 1
#Eureka服務端在收到最後一次心跳後等待時間上限,單位爲秒(默認90s,超時將剔除服務
#lease-expiration-duration-in-seconds: 2
4、此時測試http://localhost:8001/payment/get/1 、http://localhost:8001/payment/lb 和 http://localhost:9527/payment/get/1 、http://localhost:9527/payment/lb得到的結果是一樣是,說明我們的9527gateway已經實現了路由功能
而Gateway網關實現路由有兩種方法:
第一種就是yml配置方式,上述就是這一種
第二種就是在代碼中注入RouteLocator的bean,代碼如下:
package com.king.springcloud.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* created by king on 2020/4/23 2:21 下午
* 配置了一個id爲myroutebaidu1的路由規則,
* 當訪問地址http://localhost:9527/guone時會自動轉發到地址: http://news.baidu.com/guonei
*/
@Configuration
public class GatewayConfiguration {
@Bean
public RouteLocator customerRouterLocator1(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("myroutebaidu1", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
@Bean
public RouteLocator customerRouterLocator2(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("myroutebaidu2", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build();
return routes.build();
}
//注意:此處的path需要和uri的最後路徑保持一致,不一致會報錯找不到的,這個game就是反例~~
@Bean
public RouteLocator customerRouterLocator3(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("myroutebaidu3", r -> r.path("/mygame").uri("http://news.baidu.com/game")).build();
return routes.build();
}
}
可以實現路由到百度新聞的某個標籤模塊
上面是gateway實現路由的簡單配置,默認情況下Gateway會根據註冊中心註冊的服務列表,以註冊中心上微服務名爲路徑創建動態路由進行轉發,從而實現動態路由的功能。下面給出了動態路由創建配置:
1、動態路由配置yml,修改部分:
①是添加了discovery.locator.enabled:true開啓了動態路由功能
②是修改uri爲 lb://開頭 + 註冊中心微服務名 (注意服務名小寫) (lb代表注從註冊中心獲取微服務,有LoadBalance功能)
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #開啓從註冊中心動態創建路由的功能,利用微服務名進行路由
routes:
- id: payment_routh1 #路由的ID,沒有固定規則但要求唯一,建議配合服務名
uri: lb://cloud-payment-service #uri以lb://開頭(lb代表從註冊中心獲取服務)
# uri: http://localhost:8001 #匹配後提供服務的路由地址
predicates:
- Path=/payment/get/** #斷言,路徑相匹配的進行路由
- id: payment_routh2 #路由的ID,沒有固定規則但要求唯一,建議配合服務名
uri: lb://cloud-payment-service #uri以lb://開頭(lb代表從註冊中心獲取服務)
# uri: http://localhost:8001 #匹配後提供服務的路由地址
predicates:
- Path=/payment/lb/** #斷言,路徑相匹配的進行路由
2、啓動eureka7001、provider8001、provider8002和 gateway9527服務,然後測試訪問http://localhost:9527/payment/get/1,根據響應的結果端口號8001和8002輪訓出現,即可確定實現了動態路由功能。
Predicates(斷言)
Predicate就是爲了實現一組匹配規則,讓請求過來找到對應的Route進行處理。
下面舉例一下 predicates斷言的幾種類型:
- Loaded RoutePredicateFactory [After] :時間點之後服務可訪問
- Loaded RoutePredicateFactory [Before] :時間點之前服務可訪問
- Loaded RoutePredicateFactory [Between] :兩個之間點之間服務可訪問,
datetime2
參數必須是後於datetime1
- Loaded RoutePredicateFactory [Cookie] :請求服務需要帶cookie
- 此處可使用curl來進行測試 curl http://localhost:9527/payment/lb --Cookie "username=king"
- Loaded RoutePredicateFactory [Header] :請求服務需要帶header
- 請求頭要有X-Request-Id屬性並且值爲整數的正則表達式 curl http://localhost:9527/payment/lb -H "X-Request-Id:1234"
- Loaded RoutePredicateFactory [Host] :請求頭中的header中需要包含host的內容爲somehost.org等這些
- curl http://localhost:9527/payment/lb -H "host:www.anotherhost.org"
- curl http://localhost:9527/payment/lb -H "host:www.somehost.org"
- Loaded RoutePredicateFactory [Method] :請求爲post請求
- curl http://localhost:9527/payment/lb -X POST
- Loaded RoutePredicateFactory [Path] : 路徑相匹配的進行路由
- Loaded RoutePredicateFactory [Query] :要有參數名稱並且是正整數才能路由 沒演示成功
- Loaded RoutePredicateFactory [ReadBodyPredicateFactory] :官網沒看見,不做解釋了
- Loaded RoutePredicateFactory [RemoteAddr] :需要ip在1-24區間的可以訪問通過,比如 192.168.1.10
- Loaded RoutePredicateFactory [Weight] :這條路線會將此百分比的流量轉發到這裏,與其他配合服務使用。
- Loaded RoutePredicateFactory [CloudFoundryRouteService] :官網沒找到,不做解釋了
這裏官網給出的時間點是美國時區的,所以我們需要工具轉換一下爲中國/上海時區
import java.time.ZonedDateTime;
/**
* created by king on 2020/4/23 3:44 下午
*/
public class T1 {
public static void main(String[] args) {
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
//輸出爲 2020-04-23T15:45:46.581+08:00[Asia/Shanghai]
}
}
yml的例子我就直接全粘貼了,不再一個個去操作了
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #開啓從註冊中心動態創建路由的功能,利用微服務名進行路由
routes:
- id: payment_routh1 #路由的ID,沒有固定規則但要求唯一,建議配合服務名
uri: lb://cloud-payment-service #uri以lb://開頭(lb代表從註冊中心獲取服務)
# uri: http://localhost:8001 #匹配後提供服務的路由地址
predicates:
- Path=/payment/get/** #斷言,路徑相匹配的進行路由
# - Weight=group2, 8
- id: payment_routh2 #路由的ID,沒有固定規則但要求唯一,建議配合服務名
uri: lb://cloud-payment-service #uri以lb://開頭(lb代表從註冊中心獲取服務)
# uri: http://localhost:8001 #匹配後提供服務的路由地址
predicates:
- Path=/payment/lb/** #斷言,路徑相匹配的進行路由
# - After=2020-04-23T15:45:46.581+08:00[Asia/Shanghai] #此時間點後才能訪問
# - Before=2020-04-23T18:45:46.581+08:00[Asia/Shanghai] #此時間點前才能訪問
# - Between=2020-04-23T15:45:46.581+08:00[Asia/Shanghai],2020-04-24T15:45:46.581+08:00[Asia/Shanghai] #datetime1和datetime2區間請求可訪問。datetime2參數必須是後於datetime1
# - Cookie=username,king #請求服務需要帶cookie,此處可使用curl來進行測試 curl http://localhost:9527/payment/lb --Cookie "username=king"
# - Header=X-Request-Id, \d+ #請求服務需要帶header,請求頭要有X-Request-Id屬性並且值爲整數的正則表達式 curl http://localhost:9527/payment/lb -H "X-Request-Id:1234"
# - Host=**.somehost.org,**.anotherhost.org #請求頭中的header中需要包含host的內容爲somehost.org等這些 curl http://localhost:9527/payment/lb -H "host:www.anotherhost.org" 或 curl http://localhost:9527/payment/lb -H "host:www.somehost.org"
# - Method=POST #請求爲post請求 curl http://localhost:9527/payment/lb -X POST
# - Query=username, \d+ #要有參數名稱並且是正整數才能路由 沒演示成功 curl http://localhost:9527/payment/lb?username=1,但是瀏覽器輸入請求這個地址成功測試完成
# - RemoteAddr=192.168.1.1/24 #需要ip在1-24區間的可以訪問通過,比如 192.168.1.10
# - Weight=group1, 2 #這條路線會將大約80%的流量轉發到上邊兒,將大約20%的流量轉發到這裏,與其他配合服務使用。
Filter(過濾)
路由過濾器可用於修改進入的HTTP請求和返回的HTTP響應,路由過濾器只能指定路由進行使用。Spring Cloud Gateway內置了多種路由過濾器,他們都由GatewayFilter的工廠 類來產生。
springcloud gateway的Filter的 :
- 生命週期有兩個 pre(業務邏輯之前) 和 post(業務邏輯之後)
- 種類有兩種Gateway Filter 和 GlobalFilter
一般我們是通過實現自定義全局GlobalFilter過濾器,來完成操作的
自定義全局GlobalFilter過濾器搭建演示:
1、新建MyLogGlobalFilter,在類上加註解@Component ,並實現 GlobalFilter, Ordered兩個接口,並自定義攔截規則
- 先通過exchange獲取參數
- 驗證參數是否合法
- 參數不合法:通過exchange的response設置狀態碼,然後響應出去
- 完成操作後通過 chain.filter(exchange)過濾鏈 傳遞下去
package com.king.springcloud.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
/**
* created by king on 2020/4/23 5:05 下午
*/
@Component
@Slf4j
public class MyLogGlobalFilter implements GlobalFilter, Ordered {
/**
* 1.先通過exchange獲取參數
* 2.驗證參數是否合法
* 參數不合法:通過exchange的response設置狀態碼,然後響應出去
* 3.完成操作後通過 chain.filter(exchange)過濾鏈 傳遞下去
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("*********come in MyLogGlobalFilter: "+new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname ==null){
log.info("uname 爲空,非法用戶,請出去~~~~");
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return response.setComplete();
}
return chain.filter(exchange);
}
//加載過濾器的順序,一般數字越小,優先級越高
@Override
public int getOrder() {
return 0;
}
}
2、通過url請求測試Filter是否成功 http://localhost:9527/payment/lb?uname=asga、http://localhost:9527/payment/lb?u=asga
通過uname有值的得到放行,而沒有uname參數的直接返回錯誤~
到此,springcloud 的Gateway 新網關的演示結束了~後續有新的會繼續補充~~~