Spring Cloud Zuul:API網(wǎng)關服務
Spring Cloud Zuul 是Spring Cloud Netflix 子項目的核心組件之一,可以作為微服務架構(gòu)中的API網(wǎng)關使用,支持動態(tài)路由與過濾功能,本文將對其用法進行詳細介紹。
API網(wǎng)關為微服務架構(gòu)中的服務提供了統(tǒng)一的訪問入口,客戶端通過API網(wǎng)關訪問相關服務。API網(wǎng)關的定義類似于設計模式中的門面模式,它相當于整個微服務架構(gòu)中的門面,所有客戶端的訪問都通過它來進行路由及過濾。它實現(xiàn)了請求路由、負載均衡、校驗過濾、服務容錯、服務聚合等功能。
這里我們創(chuàng)建一個zuul-proxy模塊來演示zuul的常用功能。
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency>Copy to clipboardErrorCopied
server:
? port: 8801
spring:
? application:
? ? name: zuul-proxy
eureka:
? client:
? ? register-with-eureka: true
? ? fetch-registry: true
? ? service-url:
? ? ? defaultZone: http://localhost:8001/eureka/Copy to clipboardErrorCopied
在啟動類上添加@EnableZuulProxy注解來啟用Zuul的API網(wǎng)關功能
@EnableZuulProxy@EnableDiscoveryClient@SpringBootApplicationpublicclassZuulProxyApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ZuulProxyApplication.class,args);}}Copy to clipboardErrorCopied
這里我們通過啟動eureka-server,兩個user-service,feign-service和zuul-proxy來演示Zuul的常用功能,啟動后注冊中心顯示如下。

我們可以通過修改application.yml中的配置來配置路由規(guī)則,這里我們將匹配/userService/**的請求路由到user-service服務上去,匹配/feignService/**的請求路由到feign-service上去。
zuul:
? routes: #給服務配置路由
? ? user-service:
? ? ? path: /userService/**
? ? feign-service:
? ? ? path: /feignService/**Copy to clipboardErrorCopied
訪問http://localhost:8801/userService/user/1可以發(fā)現(xiàn)請求路由到了user-service上了;
訪問http://localhost:8801/feignService/user/1可以發(fā)現(xiàn)請求路由到了feign-service上了。
Zuul和Eureka結(jié)合使用,可以實現(xiàn)路由的自動配置,自動配置的路由以服務名稱為匹配路徑,相當于如下配置:
zuul:
? routes: #給服務配置路由
? ? user-service:
? ? ? path: /user-service/**
? ? feign-service:
? ? ? path: /feign-service/**Copy to clipboardErrorCopied
訪問http://localhost:8801/user-service/user/1同樣可以路由到了user-service上了;
訪問http://localhost:8801/feign-service/user/1同樣可以路由到了feign-service上了。
如果不想使用默認的路由規(guī)則,可以添加以下配置來忽略默認路由配置:
zuul:
? ignored-services: user-service,feign-service #關閉默認路由配置Copy to clipboardErrorCopied
多次調(diào)用http://localhost:8801/user-service/user/1進行測試,可以發(fā)現(xiàn)運行在8201和8202的user-service服務交替打印如下信息。
2019-10-0510:31:58.738? INFO11520---[nio-8202-exec-5]c.macro.cloud.controller.UserController:根據(jù)id獲取用戶信息,用戶名稱為:macro2019-10-0510:32:00.356? INFO11520---[nio-8202-exec-6]c.macro.cloud.controller.UserController:根據(jù)id獲取用戶信息,用戶名稱為:macroCopy to clipboardErrorCopied
我們可以通過以下配置來給網(wǎng)關路徑添加前綴,此處添加了/proxy前綴,這樣我們需要訪問http://localhost:8801/proxy/user-service/user/1才能訪問到user-service中的接口。
zuul:
? prefix: /proxy #給網(wǎng)關路由添加前綴Copy to clipboardErrorCopied
Zuul在請求路由時,默認會過濾掉一些敏感的頭信息,以下配置可以防止路由時的Cookie及Authorization的丟失:
zuul:
? sensitive-headers: Cookie,Set-Cookie,Authorization #配置過濾敏感的請求頭信息,設置為空就不會過濾Copy to clipboardErrorCopied
Zuul在請求路由時,不會設置最初的host頭信息,以下配置可以解決:
zuul:
? add-host-header: true #設置為true重定向是會添加host請求頭Copy to clipboardErrorCopied
我們可以通過SpringBoot Actuator來查看Zuul中的路由信息。
在pom.xml中添加相關依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>Copy to clipboardErrorCopied
修改application.yaml配置文件,開啟查看路由的端點:
management:
? endpoints:
? ? web:
? ? ? exposure:
? ? ? ? include: 'routes'Copy to clipboardErrorCopied
通過訪問http://localhost:8801/actuator/routes查看簡單路由信息:

通過訪問http://localhost:8801/actuator/routes/details查看詳細路由信息:

路由與過濾是Zuul的兩大核心功能,路由功能負責將外部請求轉(zhuǎn)發(fā)到具體的服務實例上去,是實現(xiàn)統(tǒng)一訪問入口的基礎,過濾功能負責對請求過程進行額外的處理,是請求校驗過濾及服務聚合的基礎。
Zuul中有以下幾種典型的過濾器類型。
pre:在請求被路由到目標服務前執(zhí)行,比如權限校驗、打印日志等功能;
routing:在請求被路由到目標服務時執(zhí)行,這是使用Apache HttpClient或Netflix Ribbon構(gòu)建和發(fā)送原始HTTP請求的地方;
post:在請求被路由到目標服務后執(zhí)行,比如給目標服務的響應添加頭信息,收集統(tǒng)計數(shù)據(jù)等功能;
error:請求在其他階段發(fā)生錯誤時執(zhí)行。
下圖描述了一個HTTP請求到達API網(wǎng)關后,如何在各種不同類型的過濾器中流轉(zhuǎn)的過程。
接下來我們自定義一個過濾器來演示下過濾器的作用。
這是一個前置過濾器,用于在請求路由到目標服務前打印請求日志。
/**
* Created by macro on 2019/9/9.
*/@ComponentpublicclassPreLogFilterextendsZuulFilter{privateLoggerLOGGER=LoggerFactory.getLogger(this.getClass());/**
? ? * 過濾器類型,有pre、routing、post、error四種。
? ? */@OverridepublicStringfilterType(){return"pre";}/**
? ? * 過濾器執(zhí)行順序,數(shù)值越小優(yōu)先級越高。
? ? */@OverridepublicintfilterOrder(){return1;}/**
? ? * 是否進行過濾,返回true會執(zhí)行過濾。
? ? */@OverridepublicbooleanshouldFilter(){returntrue;}/**
? ? * 自定義的過濾器邏輯,當shouldFilter()返回true時會執(zhí)行。
? ? */@OverridepublicObjectrun()throwsZuulException{RequestContextrequestContext=RequestContext.getCurrentContext();HttpServletRequestrequest=requestContext.getRequest();Stringhost=request.getRemoteHost();Stringmethod=request.getMethod();Stringuri=request.getRequestURI();LOGGER.info("Remote host:{},method:{},uri:{}",host,method,uri);returnnull;}}Copy to clipboardErrorCopied
添加過濾器后,我們訪問http://localhost:8801/user-service/user/1測試下,會打印如下日志。
2019-10-0515:13:10.232? INFO11040---[nio-8801-exec-7]com.macro.cloud.filter.PreLogFilter:Remote host:0:0:0:0:0:0:0:1,method:GET,uri:/user-service/user/1Copy to clipboardErrorCopied
過濾器名稱過濾類型優(yōu)先級過濾器的作用
ServletDetectionFilterpre-3檢測當前請求是通過DispatcherServlet處理運行的還是ZuulServlet運行處理的。
Servlet30WrapperFilterpre-2對原始的HttpServletRequest進行包裝。
FormBodyWrapperFilterpre-1將Content-Type為application/x-www-form-urlencoded或multipart/form-data的請求包裝成FormBodyRequestWrapper對象。
DebugFilterroute1根據(jù)zuul.debug.request的配置來決定是否打印debug日志。
PreDecorationFilterroute5對當前請求進行預處理以便執(zhí)行后續(xù)操作。
RibbonRoutingFilterroute10通過Ribbon和Hystrix來向服務實例發(fā)起請求,并將請求結(jié)果進行返回。
SimpleHostRoutingFilterroute100只對請求上下文中有routeHost參數(shù)的進行處理,直接使用HttpClient向routeHost對應的物理地址進行轉(zhuǎn)發(fā)。
SendForwardFilterroute500只對請求上下文中有forward.to參數(shù)的進行處理,進行本地跳轉(zhuǎn)。
SendErrorFilterpost0當其他過濾器內(nèi)部發(fā)生異常時的會由它來進行處理,產(chǎn)生錯誤響應。
SendResponseFilterpost1000利用請求上下文的響應信息來組織請求成功的響應內(nèi)容。
我們可以對過濾器進行禁用的配置,配置格式如下:
zuul:
? filterClassName:
? ? filter:
? ? ? disable: true Copy to clipboardErrorCopied
以下是禁用PreLogFilter的示例配置:
zuul:
? PreLogFilter:
? ? pre:
? ? ? disable: true Copy to clipboardErrorCopied
由于Zuul自動集成了Ribbon和Hystrix,所以Zuul天生就有負載均衡和服務容錯能力,我們可以通過Ribbon和Hystrix的配置來配置Zuul中的相應功能。
可以使用Hystrix的配置來設置路由轉(zhuǎn)發(fā)時HystrixCommand的執(zhí)行超時時間:
hystrix:
? command: #用于控制HystrixCommand的行為
? ? default:
? ? ? execution:
? ? ? ? isolation:
? ? ? ? ? thread:
? ? ? ? ? ? timeoutInMilliseconds: 1000 #配置HystrixCommand執(zhí)行的超時時間,執(zhí)行超過該時間會進行服務降級處理Copy to clipboardErrorCopied
可以使用Ribbon的配置來設置路由轉(zhuǎn)發(fā)時請求連接及處理的超時時間:
ribbon: #全局配置
? ConnectTimeout: 1000 #服務請求連接超時時間(毫秒)
? ReadTimeout: 3000 #服務請求處理超時時間(毫秒)Copy to clipboardErrorCopied
zuul:
? routes: #給服務配置路由
? ? user-service:
? ? ? path: /userService/**
? ? feign-service:
? ? ? path: /feignService/**
? ignored-services: user-service,feign-service #關閉默認路由配置
? prefix: /proxy #給網(wǎng)關路由添加前綴
? sensitive-headers: Cookie,Set-Cookie,Authorization #配置過濾敏感的請求頭信息,設置為空就不會過濾
? add-host-header: true #設置為true重定向是會添加host請求頭
? retryable: true # 關閉重試機制
? PreLogFilter:
? ? pre:
? ? ? disable: false #控制是否啟用過濾器Copy to clipboardErrorCopied
springcloud-learning
├── eureka-server -- eureka注冊中心
├── user-service -- 提供User對象CRUD接口的服務
├── feign-service -- feign服務調(diào)用測試服務
└── zuul-proxy -- zuul作為網(wǎng)關的測試服務