SpringCloud學習筆記(六):使用Zuul實作路由和請求過濾
- Zuul的作用
-
- 沒有使用Zuul時的情況
- 使用了Zuul時的情況
- 實作
-
- 路由實作
- 過濾器實作
Zuul的作用
沒有使用Zuul時的情況
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLx0keONTTE10MFpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL0ETNyATMxUTM3ETMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
通過先前的内容我們可以很容易實作如上圖所示的架構。在正常的項目中,調用服務前基本都需要經過鑒權之類的操作。使用上面的架構,每個開放服務都需要實作鑒權,不僅提高了開發的複雜程度,而且使得代碼變得備援。是以我們需要一個元件來搭建開放服務和使用者端之間的橋梁,在這一層面上實作代理、鑒權等操作。Netfilx Zuul就能達成這一目的。
使用了Zuul時的情況
在原有架構中加入Zuul元件後,架構如上圖所示,使用了Zuul之後,使用者調用我們系統服務的請求将會先到達Zuul網關,再由網關對不同的請求進行路由轉發。在執行轉發操作之前,Zuul可以通過過濾器執行一系列代碼,實作請求過濾,達到類似Spring MVC中攔截器的效果。此時各個微服務的開發人員不需要在關注諸如鑒權之類的問題,鑒權由Zuul統一處理,隻有鑒權通過的請求才會被路由,否則Zuul直接過濾掉該請求。
實作
路由實作
我們在項目中建立一個Maven子產品,我這裡給子產品命名為zuulgateway,在子產品下pom.xml中新增如下的依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<!-- 導入Spring Cloud的依賴管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--整合eureka用戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<!--整合zuul網關-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
然後在resource目錄下建立application.yml,配置一下Zuul的路由轉發規則。application.yml内容如下
server:
port: 8070 #端口号
spring:
application:
name: app-zuul-gateway #應用名
##配置eureka,從注冊中心擷取服務位址
eureka:
client:
service-url:
defaultZone: http://localhost:9090/eureka/
###因為該應用為服務提供者,是eureka的一個用戶端,需要注冊到注冊中心
register-with-eureka: true
###是否需要從eureka上檢索服務
fetch-registry: true
instance:
prefer-ip-address: true #将自己的ip位址注冊到Eureka服務中
ip-address: 127.0.0.1
instance-id: ${spring.application.name}###${server.port} #指定執行個體ID
##zuul路由規則
zuul:
host:
connect-timeout-millis: 15000 #HTTP連接配接逾時大于Hystrix的逾時時間
socket-timeout-millis: 60000 #socket逾時
routes:
cargo-service: #這是個随便取的名字,用于識别不同的路由,從原位址到目的位址
path: /cargo-service/** #通路的位址
serviceid: app-cargo #轉發到哪個微服務
cargolist-service:
path: /cargolist-service/**
serviceid: app-cargolist
ribbon: #設定ribbon的逾時時間小于zuul的逾時時間
ReadTimeout: 10000
ConnectTimeout: 10000
這裡注意一下,一定要讓Zuul的逾時時間大于Ribbon的逾時時間,否則Zuul無法正常工作,請求會失敗。
然後在src/main/java下建立一個runner包,在包下建立啟動類,類上添加@EnableZuulProxy注解,表示啟動Zuul,啟動微服務即可測試檢視效果。啟動類完整代碼如下
package runner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy//啟用Zuul
public class ZuulGatewayApp {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApp.class, args);
}
}
微服務啟動完成後,通路http://localhost:8070/cargolist-service/cargoList/123,通過Zuul微服務來調用app-cargo-list微服務,頁面正常傳回結果,Zuul在此時已經起到了路由代理的作用,Zuul的基本配置就完成了。
過濾器實作
Zuul的過濾器實作同樣非常簡單,隻需要建立一個過濾器類,并且讓SpringBoot架構掃描到它就可以實作了,我們在子產品下再建立一個filter包用來存放過濾器類,建立一個TokenZuulFilter類,并繼承ZuulFilter。ZuulFilter是一個抽象方法,裡面定義了一系列攔截器執行過程。TokenZuulFilter類完整代碼如下。
package filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
@Component
public class TokenZuulFilter extends ZuulFilter {
@Override
public String filterType() {
//pre:請求被路由前執行
//routing:路由請求調用時執行
//post:路由完成或路由送出後出錯後傳回前執行
//error:處理請求時發生錯誤執行
return "pre";
}
@Override
public int filterOrder() {
//數字越小優先級越高
return 0;
}
@Override
public boolean shouldFilter() {
//true執行,false不執行
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token");
if(StringUtils.isEmpty(token)||!"123".equals(token)){//檢查token
requestContext.setSendZuulResponse(false); // 過濾該請求,不對其進行路由
requestContext.setResponseStatusCode(401); // 設定響應狀态碼
requestContext.setResponseBody("TOKEN ERROR"); // 設定響應狀态碼
return null;
}
return null;
}
}
在啟動類@SpringBootApplication注解加入包掃描的參數,添加filter包,重新開機微服務即可測試檢視結果。
通路http://localhost:8070/cargolist-service/cargoList/123,發現頁面傳回TOKEN ERROR,F12檢視請求狀态碼為401。我們在URL上加入參數再通路http://localhost:8070/cargolist-service/cargoList/123?token=123,此時發現頁面可以正常傳回,至此Zuul過濾器實作就完成了。