版本號 | 作者 | SpringCloud Gateway版本號 | 備註 | |
---|---|---|---|---|
v0.0.1 | 若布與宮合 | 8416837 | 2.1.0.RELEASE | 反向代理 負載均衡 服務發現 監控埋點 |
原創文章轉載請註明出處 https://blog.csdn.net/cc007cc009/article/details/92570640
用途
- 安全
- 微服務代理
- 監控/埋點
- 限流
- 路徑重寫
技術棧
- 動態路由
- 熔斷器
- 服務模式
過濾器
簡介
- 過濾器種類
內置
自定義 - Filter責任鏈
過濾器責任鏈,管理過濾器執行順序。 - 過濾器工作原理
過濾器可以在發送代理請求之前或之後執行.
內置的系統過濾器
- PrefixPath
- StripPrefix
springBoot yaml配置文件:
routes:
- id: routetest1
uri: https://www.baidu.com # 上游
predicates:
- Path=/service/baidu/s # 轉發正確. 路由路徑
filters:
- StripPrefix=2
路由定位器
- Demo
關鍵配置
spring:
cloud:
gateway:
discovery:
# 網關發現服務開啓
locator:
# 默認關閉
enabled: true
# 默認關閉
lower-case-service-id: true
微服務API地址http://localhost:8002/system/sysDept/list
經網關代理後的地址:http://localhost:9999/service-name/system/sysDept/list
,service-name是服務註冊名稱。這時在網關添加過濾器,即可攔截請求了。
注意:通過服務名訪問,不要定義路由特指。
通過網關訪問成功:
注:localhost:9999爲本地網關url。
- 負載均衡
通過網關訪問微服務時,默認負載均衡。
過濾器
@Slf4j
public class RequestTimeFilter implements GatewayFilter, Ordered {
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.debug("{}=={},{}=={}",
"請求路徑", exchange.getRequest().getURI().getRawPath(),
"消耗時間 ", (System.currentTimeMillis() - startTime) + "ms");
}
})
);
}
@Override
public int getOrder() { // 亦可註解
// order值與優先級成反比
return 0;
}
}
啓動訪問,過濾器沒起效,這是因爲還需要將該過濾器加入責任鏈。見下文
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
// 通過訪問.../cecjx跳轉到cecjx.com/cecjx cecjx路徑會轉交過去 PATH將作爲遠程應用的path
return builder.routes()
.route(p -> p
.path("/**/**")
.filters(f -> f.filter(new RequestTimeFilter()))
.uri("http://localhost:8002")
.order(0)// 起效了
.id("customer_filter_router")
)
.build();// 熔斷測試要去掉contextPath
}
這樣就起效了。
14:56:09 [reactor-http-nio-3] DEBUG c.j.gateway.filter.RequestTimeFilter - 請求路徑==/system/sysDept/list,消耗時間 ==17ms
14:56:19 [reactor-http-nio-3] DEBUG c.j.gateway.filter.RequestTimeFilter - 請求路徑==/system/sysDept/list3,消耗時間 ==15ms
- 全局過濾器
// 全局過濾器 優先執行
@Component
@Slf4j
public class TokenFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (token == null || token.isEmpty()) {
log.debug("token 爲空,無法進行訪問.");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
不傳Token,無法訪問
15:49:37 [reactor-http-nio-2] DEBUG com.jxbd.gateway.filter.TokenFilter - token 爲空,無法進行訪問.
傳Token,訪問成功.Token應加密到Https請求頭.
自帶管理接口
# 配置
management:
endpoints:
web:
exposure:
include: "*"
訪問http://localhost:9999/actuator/gateway/routes
↓
不過這裏我們不打算用自帶的Restful接口,一來官方文檔也沒說新增網關參數怎麼傳,再者我們也不希望在網關暴露這些接口,第三路由信息也需要持久化起來.參照org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint
,我們自己編程來動態改變網關。
跨域
熔斷器
在路由配置熔斷器
.route(p -> p
.host("*.hystrix.com")
.filters(f -> f
.hystrix(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri("http://httpbin3.org"))
curl --dump-header - --header Host:www.hystrix.com http://localhost:9999/any # 因上游地址訪問不通,觸發熔斷
因爲訪問不到http://httpbin3.org/any
,所以會觸發熔斷。
重試
限流
異常處理
* Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.http.codec.ServerCodecConfigurer' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
解決方法:
去除spring-web依賴
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
路由
路由種類:
路徑
參數
主機
請求頭
範圍路由
我們主要使用路徑路由,其實是做了個路徑的反向代理。
將路徑路由轉發到上游服務器,與上游服務器的url拼接起來。
動態路由
- 是網關的核心功能之一,在不重啓網關的情況下,即可更新路由和路由權限。
代碼解釋
RouteDefinitionLocator
public interface RouteDefinitionLocator { // 實現該接口:實時獲取路由.不好用,因爲調用時讀取全部路由太耗資源了.
Flux<RouteDefinition> getRouteDefinitions(); // 不過調用時,並不是每次都走這個方法.
}
同時也要考慮網關集羣時,路由亦需要使用中心化的存儲器存儲起來:redis、etcd、postgresql
RouteDefinitionWriter
public interface RouteDefinitionWriter { // 開發者實現該接口
Mono<Void> save(Mono<RouteDefinition> route); // 保存單個路由,只是將路由存入客戶自定義的數據庫.
Mono<Void> delete(Mono<String> routeId); // 刪除單個路由
}
RouteDefinitionRepository
public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
} // 繼承上述兩個接口
ApplicationEventPublisherAware
public interface ApplicationEventPublisherAware extends Aware {
void setApplicationEventPublisher(ApplicationEventPublisher var1); // 監聽
}
public interface Aware {
} // 空的
ApplicationEventPublisher
@FunctionalInterface
public interface ApplicationEventPublisher { // 事件監聽 監聽路由改變
default void publishEvent(ApplicationEvent event) { // ApplicationEvent 它的超類是EventObject
this.publishEvent((Object)event);
}
void publishEvent(Object var1);
}
RouteDefinition
按這個數據結構定義路由,並存儲待使用。
@Validated
public class RouteDefinition {
@NotEmpty
private String id = UUID.randomUUID().toString(); // 默認uuid,一般會覆蓋這個id,以便於後續操作.
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList<>();
@Valid
private List<FilterDefinition> filters = new ArrayList<>();
@NotNull
private URI uri;
private int order = 0;
// ... 片段 ...
}