網關(GateWay)
第一代網關zuul 1.X
Netflix開源的網關,使用Java開發,基于Servlet架構建構,便于二次開發。因為基于Servlet内部延遲嚴重,并發場景不友好,一個線程隻能處理一次連接配接請求。
但由于 zuul 采取的是 servlet 2.5 阻塞IO,性能較低且 zuul2 遲遲未釋出,目前不再推薦使用。
spring 推出的 “GateWay” 網關元件
Spring Cloud Gateway 使用的Webflux中的reactor-netty響應式程式設計元件,底層使用了Netty通訊架構。
Yaml 配置:
路由(Route)是GateWay中最基本的元件之一,表示一個具體的路由資訊載體,主要由下面幾個部分組成:
- id:路由唯一辨別,差別于其他的route
- url: 路由指向的目的地URL,用戶端請求最終被轉發到的微服務
- order: 用于多個Route之間的排序,數值越小越靠前,比對優先級越高
- predicate:斷言的作用是進行條件判斷,隻有斷言為true,才執行路由
- filter: 過濾器用于修改請求和響應資訊
server:
port: 9527
spring:
application:
name: 微服務名
cloud:
gateway:
routes:
- id: 類似資料庫主鍵字段,全局唯一即可
# uri: http://localhost:8001 # 比對後提空服務的路由位址
uri: lb://cloud-payment-service # 注冊中心中的服務名
predicates:
- Path=/payment/get/** # 斷言,路徑相比對的進行路由跳轉,controller請求位址
注冊中心配置...
作用:
- 反向代理
- 鑒權
- 流量控制
- 熔斷
- 日志監控
三大核心概念
一. Route(路由)
路由是建構網關的基本子產品,它由 ID ,目标 URI ,一系列的斷言和過濾器組成,如果斷言為 true 則比對該路由。
二. Predicate(斷言)
參考的是 java 8的 java.util.function.Predicate 開發人員可以比對 HTTP 請求中的所有内容(例如請求頭或請求參數),如果請求與斷言相比對則進行路由。在 Yaml 檔案中配置在 Predicates 結點下
1) After:在某時間點之後才可以進行通路
// 擷取格式化時區時間的 Java 方法
public static void main(String[] args) {
ZonedDateTime zbj = ZonedDateTime.now(); // 根據系統的預設時區擷取
System.out.print(zbj); // 2022-10-04T16:49:23.334+08:00[Asia/Shanghai]
}
# Yaml 中的配置
cloud:
gateway:
routes:
predicates: - After=2022-10-04T16:49:23.334+08:00[Asia/Shanghai]
2)Between、Before 時間同理
3)Cookie:攜帶 Cookie 才可以通路,以攜帶使用者名和zzyy字段為例
# Yaml 中的配置
cloud:
gateway:
routes:
predicates:
- Cookie=username,zzyy
4)Header:請求頭中攜帶指定字段
5)Host:請求需為指定主機(ip)
6)Method:請求需為指定請求類型(GET)
三. Filter(過濾)
指的是 Spring 架構中 GatewayFilter 的執行個體,使用過濾器,可以在請求被路由前或者之後對請求進行修改。
1) 生命周期 Only Two
pre:請求之前
post:請求之後
2) 種類 Only Two
GatewayFilter:單一網關過濾器
GlobalFilter:全局網關過濾器
3)自定義過濾器:
/**
* 全局網關過濾器配置,以“請求頭”必須攜帶使用者名為例
* Mono 相當于 MVC 中的 ModelAndView
*
* @Author: 魏一yi
* @Date: 2022/10/4 14:58
*/
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("******come in MyLogGateWayFilter" + new Date());
// exchange.getRequest() 擷取的對象類似于HttpServletRequest
// 請求必須帶uname
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname == null) {
log.info("******使用者名為空,非法使用者");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 數字越小優先級越高
return 0;
}
}