Spring boot zuul 閘道器
Zuul作為微服務系統的閘道器元件,用於構建邊界服務,致力於動態的路由、過濾、監控、彈性伸縮和安全。
其中Zuul、Ribbon以及Eureka的結合使用可以實現智慧路由和負載均衡的功能,閘道器將所有的服務的API介面統一聚合,統一對外暴露,外界呼叫API的介面的時候,不需要知道微服務系統中各服務相關呼叫的複雜性,保護了內部微服務單元的API介面,閘道器可以做使用者身份認證和許可權認證,防止非法請求操作api介面,閘道器可以實現監控功能,實時日誌輸出,對請求進行記錄,閘道器可以實現流量監控,在高流量監控的情況下,對服務降級,API介面從內部服務分離出來,方便做測試
Zuul通過Servlet來實現,通過自定義的ZuulServlet來對請求進行控制,核心的是一系列過濾器,可以在http請求的發起和相應返回期間執行一系列的過濾器,Zuul採取了動態處理、編譯等這些過濾器,過濾器之間不能直接通訊,而是通過RequestContext(靜態類)物件來實現共享資料,每個請求都會建立一個RequetsContext物件,RequestContext中又ThreadLocal變數來記錄每個Request所需要傳遞的資料,底層使用的是ThreadLocalMap來進行儲存資料的。
Zuul大部分功能都是通過過濾器來實現的,Zuul中定義了四種標準過濾器型別,這些過濾器型別的對應於請求的的典型生命週期。
1,PRE:這種過濾器在請求到達路由之前被呼叫,我們可以利用這種過濾器實現身份驗證,在叢集中選擇請求的微服務,記錄除錯資訊等。
2,ROUTING:這種過濾器將請求通過路由到達微服務,這種過濾器用於構建傳送給微服務的請求,並使用Apache HttpClient或Netfinx Ribbon請求到微服務。
3,POST:這種過濾器在請求被路由到微服務以後執行,這種過濾器可用來響應新增標準的HTTP Header、收集統計資訊和指標,將響應從微服務傳送給客戶端等。
4,ERROE:在其他階段發生錯誤時執行該過濾器。
內建的特殊過濾器:
在zuul還提供了一類特殊的過濾器,分別為:StaticResponseFilter和SurgicaDebugFilter
StaticResponseFilter:允許從Zuul本身生成響應,而不是將請求轉發到資源
SurgicaDebugFilter:允許將特定的請求路由到分隔的除錯主機或者叢集
自定義的過濾器:
除了預設的過濾器,Zuul還允許我們建立自定義的過濾器型別,例如可以定製一種STATIC型別的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務
可是有沒有注意到,如果我們不使用zuul,當我們在每個微服務增加了新的方法,都需要在閘道器層手動的增加相應的方法封裝,而Spring cloud 中的zuul很好的解決了這一問題
zuul作為閘道器層,自身也是一個微服務,跟其他服務Service-1. Service-2 。。。一樣,都需要註冊在Eureka Server上,可以相互發現,zuul都幹知道那些服務線上,同時通過配置路由規則,可以將請求轉發到指定的後端的服務上,對於一些公用的預處理,(比如:許可權認證。token合法校驗,灰度驗證時部分流量引導之類)可以放在所謂的過濾器(ZuulFilter -- 自定義的所謂的處理類)裡處理,這樣後端服務以後新增了,zuul層幾乎可以保持不變。
Spring Cloud Zuul路由是微服務架構中不可缺少的一部分,提供動態路由,監控、彈性、安全等邊緣服務,Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器
下面我們通過程式碼來了解Zuul是如何工作的:
簡單實用
1,新增依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
2,編寫配置檔案 application.yml
spring:
application:
name: gateway-service-zuul
server:
port: 8888
#增加路由規則的配置
#通過zuul.routes.<route>.path和zuul.routes.<route>.url進行配置,<route>為路由的名字,可以任意指定,但是一組path和url的路由名要相同
zuul.routes.baidu.path: /baidu/**
zuul.routes.baidu.url: http://www.baidu.com
3,設定啟動類
package com.example.springzuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class SpringzuulApplication {
public static void main(String[] args) {
SpringApplication.run(SpringzuulApplication.class, args);
}
}
啟動類上新增@EnableZuulProxy的註解,用於啟動閘道器路由,最簡單的Zuul案例就配置完成了
簡單的Zuul配置完成之後,可是上面咱們在總結的時候,發現四種標準的過濾器咱們還麼有使用到,下面講解一下四種過濾器的使用情況,在使用的時候需要自己定義一個類,繼承ZuulFilter類,並且覆蓋裡面的方法。
package com.example.springzuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import javax.servlet.http.HttpSession;
/**
* 自定義過濾器,可以實現在不同的時候進行資料的攔截,分為四種狀態
* 當請求到達服務的時候,
* 請求進入服務的時候,
* 請求到達服務之後
* 錯誤的時候
*/
public class MyFilter extends ZuulFilter{
/**
* 表示返回的型別,就是確定要進行什麼樣的操作
* @return
*/
@Override
public String filterType() {
return null;
}
/**
* 設定級別,數字越大級別越低
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 表示是否開啟當前的攔截器 false 是不開啟, true是開啟
* @return
*/
@Override
public boolean shouldFilter() {
return false;
}
/**
* 表示執行的程式碼,比如說請求的時候,進行身份的認證等..
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
// currentContext.set
HttpSession session = currentContext.getRequest().getSession();
return null;
}
}
自定義類之後,可以完成一些自己的配置
filterType:表示返回的型別,也就是需要說明你當前的攔截器是在什麼時候使用。
filterOrder:設定優先順序,因為會有很多的攔截器,並不是只有當前設定的一個攔截器,所以需要設定攔截的優先順序,數字越大表示優先順序越低
shouldFilter:表示設定是否開啟攔截器,true是開啟攔截器,false是不開啟攔截器
run:就是攔截之後需要進行的操作,可以根據上面的filterType設定的型別,進行相應的操作,在使用的時候,使用到一個RequestContext的物件,該物件在上面也說過,因為攔截器之間是沒有通訊的,所以使用RequestContet來進行資料的共享,當我在操作的時候,有一點像不明白,該物件裡面的資料是在什麼時候傳遞過來的呢,由於原始碼沒有看懂也沒有找打相似的程式碼,於是與網上尋找幫助,在看完資料之後,資料的傳輸是不是用過域來記性資料的儲存和傳輸的呢,因為RequestContext物件可以獲取到Request之後可以獲取到Session物件,將資料儲存到該物件中,可以完成在登入的時候獲取使用者的資訊,也可以完成在攔截器中完成資料的共享,不知道想法是否正確,還希望大佬給出答案。
上面是是自定義的攔截器,但是如果需要時會用自己定義的攔截器,是否還需要將自己定義的攔截器放在spring的容器中呢,所以需要配置一個java配置。
package com.example.springzuul;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 用於將自己定義的攔截器,宣告成bean注入到spring的容器中
*/
@Configuration
public class FilterConfig {
@Bean
public MyFilter myFilter() {
MyFilter myFilter = new MyFilter();
return myFilter;
}
}