博客同步至GitPress:https://gitpress.io/@yangshijie/spring_cloud_zuul
示例代碼已上傳Github: https://github.com/yshijie/spring_cloud_demo
Zuul組件簡介
Zuul組件主要是用來做動態路由轉發和請求攔截過濾等;
在客戶端訪問服務端時,經常會有一些通過驗證用戶的token或者請求頭之類的去判斷並且指定具體訪問哪些數據服務,例如不同組織機構的用戶通過請求的token的不同被服務端指定分發到不同的數據中心去查詢對應的數據;
以及在客戶端請求服務端時,服務端攔截客戶端請求,可以對請求做一些預處理等操作;
在SpringCloud工程中,創建了兩個數據服務datacenter-a和datacenter-b,以及一個註冊服務,一個分發服務,這個分發服務就是用來指定路由去訪問具體哪個數據中心的數據;
核心類
Zuul組件的核心類是ZuulFilter,通過自定義過濾類繼承該類,並重寫指定方法進行請求的攔截處理;
public class MyFilter extends ZuulFilter {
Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
return null;
}
}
filterType方法:指定在什麼地方進行攔截,可以使是請求前/後,發生錯誤時等;
pre:在路由轉發之前作用;
routing:在路由時起作用;
post:在返回結果時作用;
error:在整個路由階段,出現異常時作用;
filterOder方法:攔截的優先級,0表示最大;
shouldFliter方法:是否攔截,可以通過一些邏輯判斷是否需要進行攔截;
run方法:執行具體的攔截操作;
添加依賴
在需要進行動態路由轉發的服務(即分發服務)的pom.xml文件中加入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
動態路由轉發
通過請求地址不同將服務指向不同的子服務,例如,服務集羣中,A服務用於存儲用戶信息,B服務用於存儲相關的商品信息,在請求是通過路徑設定user/goods將查詢指向不同的服務;
配置轉發信息
啓動類註解
在需要進行動態路由轉發的服務(即分發服務)的啓動類上配置@EnableZuulProxy註解:
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class DispatchServerApplication {
public static void main(String[] args) {
SpringApplication.run(DispatchServerApplication.class, args);
}
}
application.yml文件配置
在分發服務的application.yml文件中配置需要轉發的邏輯和轉發的對應服務地址:
spring:
application:
name: dispatch-server
server:
port: 8760
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
zuul:
retryable: false
routes:
data-a:
path: /user/**
serviceId: data-server-a
data-b:
path: /goods/**
serviceId: data-server-b
請求測試
通過網關訪問:
http://localhost:8760/alarm/aaa/getData
http://localhost:8760/goods/aaa/getData
此時根據上面yml中的配置,第一個請求將自動轉發到data-server-a這個數據服務,第二個請求將自動轉發到data-server-b這個數據服務;
請求攔截過濾
4中的方法是一個比較簡單直白的做法,也可以對請求進行攔截判斷,通過請求的不同參數、或者請求頭信息等,將客戶端請求指向不同的服務;
yml配置路由信息
zuul:
routes:
data-a:
path: /**
serviceId: service-datacenter-a
data-b:
path:/**
serviceId:service-datacenter-b
過濾請求
@Value("${zuul.routes.data-a.serviceId}")
private String aa;
@Value("${zuul.routes.data-b.serviceId}")
private String bb;
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletResponse response = ctx.getResponse();
String userType = request.getParameter("userType");
String serviceId = null;
try {
if (userType != null) {
if (userType.equals("A")) {
serviceId = aa;
} else if (userType.equals("B")) {
serviceId = bb;
}
} else {
serviceId = "xxx";
}
} catch (Exception e) {
e.printStackTrace();
}
if (serviceId == null) {
//服務查詢失敗 不再路由直接返回
response.setHeader("Content-Type", "application/json;charset=UTF-8");
ctx.setSendZuulResponse(false);
ctx.setResponseBody("Error: No service");
} else {
//轉發請求
log.info("--->>> Forward to service :" + serviceId);
ctx.set("serviceId", serviceId);
}
return null;
}
在該類上添加@Configuration註解表明是個配置類;
以上做法中,訪問的地址爲同一個,但是根據傳入的參數不同,利用Zuul攔截請求,將請求重新定義定指定轉發到不同的子服務中;
請求測試
兩個子服務分別訪問地址爲:
A:http://localhost:8763/aaa/getData?userType=""
B:http://localhost:8762/aaa/getData?userType=""
通過網關訪問時,請求http://localhost:8760/aaa/getData?userType="",當參數傳入A時,請求轉發到A服務,當傳輸傳入B時,轉發到B服務;
這樣對用戶來說是無感覺的,可以將判斷標準定爲請求的token等來作爲轉發的判斷條件等;