Zuul 作為路由網關組件,在微服務架構中有著非常重要的作用,主要體現在以下 6 個方面:
- Zuul, Ribbon 以及 Eureka 相結合,可以實現智能路由和負載均衡的功能,Zuul 能夠將請求流量按某種策略分發到集群狀態的多個服務實例
- 網關將所有服務的 API 接口統一聚合,并統一對外暴露。外界系統調用 API 接口時,都是由網關對外暴露的 API 接口,外界系統不需要知道微服務系統中各服務相互調用的復雜性。微服務系統也保護了其內部微服務單元的 API 接口 , 防止其被外界直 接調用,導致服務的敏感信息對外暴露
- 網關服務可以做用戶身份認證和權限認證,防止非法請求操作 API 接口,對服務器起到保護作用
- 網關可以實現監控功能,實時日志輸出,對請求進行記錄
- 網關可以用來實現流量監控,在高流量的情況下,對服務進行降級
- API 接口從內部服務分離出來,方便做測試
Zuul 的核心是一系列過濾器,可以在 Http 請求的發起和響應返回期間執行一系列的過濾器:
- PRE 過濾器:在請求路由到具體的服務之前執行,這種類型的過濾器可以做安全驗證,例如身份驗證、 參數驗證等
- ROUTING 過濾器:用于將請求路由到具體的微服務實例。在默認情況下,它使用 Http Client 進行網絡請求
- POST 過濾器:在請求己被路由到微服務后執行。 一般情況下,用作收集統計 信息、指標,以及將響應傳輸到客戶端
- ERROR 過濾器:在其他過濾器發生錯誤時執行
Zuul 采取了動態讀取、編譯和運行這些過濾器。過濾器之間不能直接相互通信,而是通過 RequestContext 對象來共享數據,每個請求都會創建一個 RequestContext 對象。Zuul 過濾器具有以下關鍵特性:
- Type (類型): Zuul 過濾器的類型,這個類型決定了過濾器在請求的哪個階段起作用,例如 Pre、Post 階段等
- Execution Order (執行順序): 規定了過濾器的執行順序,Order 的值越小越先執行
- Criteria (標準): Filter 執行所需的條件
- Action (行動): 如果符合執行條件,則執行 Action (即邏輯代碼)
使用 Zuul
新建 spring-cloud-eureka-zuul-client
pom
<parent><artifactId>spring-cloud-parent</artifactId><groupId>com.karonda</groupId><version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion><artifactId>spring-cloud-eureka-zuul-client</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-rest</artifactId></dependency><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>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>
application.yml
server:port: 8051eureka:client:service-url:defaultZone: http://localhost:8001/eureka/spring:application:name: zuul-clientzuul:routes:hiapi:path: /hiapi/**serviceId: eureka-clientribbonapi:path: /ribbonapi/**serviceId: ribbon-clientfeignapi:path: /feignapi/**serviceId: feign-client
啟動類
@EnableZuulProxy // 開啟 Zuul
@SpringBootApplication
public class EurekaZuulClientApp {public static void main(String[] args){SpringApplication.run(EurekaZuulClientApp.class, args);}
}
測試
- 啟動 eureka-server
- 啟動 eureka-client (兩個實例:一個 8011 端口,一個 8012 端口)
- 啟動 eureka-ribbon-client
- 啟動 eureka-feign-client
- 啟動 eureka-zuul-client
多次訪問 http://localhost:8031/hiapi/hi?name=victor 可以看到 8011 和 8012 端口交替出現 (Zuul 默認 與 Ribbon 結合實現了負載均衡)
多次訪問 http://localhost:8031/ribbonapi/hi?name=victor 可以看到 8011 和 8012 端口交替出現
多次訪問 http://localhost:8031/feignapi/hi?name=victor 可以看到 8011 和 8012 端口交替出現
在 Zuul 上配置熔斷器
實現 FallbackProvider 接口
@Component
public class MyFallbackProvider implements FallbackProvider {@Overridepublic String getRoute() {return "eureka-client"; // 如果所有的路由服務都加熔斷功能,返回 "*"}@Overridepublic ClientHttpResponse fallbackResponse(String route, Throwable cause) {return new ClientHttpResponse() {@Overridepublic HttpStatus getStatusCode() throws IOException {return HttpStatus.OK;}@Overridepublic int getRawStatusCode() throws IOException {return 200;}@Overridepublic String getStatusText() throws IOException {return "OK";}@Overridepublic void close() {}@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream("error, fallback".getBytes());}@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);return headers;}};}
}
測試
- 關閉所有的 eureka-client
- 重啟 eureka-zuul-client
在 Zuul 中使用過濾器
繼承 ZuulFilter
@Component
public class MyFilter extends ZuulFilter {private static Logger logger = LoggerFactory.getLogger(MyFilter.class);@Overridepublic String filterType() {return PRE_TYPE;}@Overridepublic int filterOrder() {return 0;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();HttpServletRequest request = ctx.getRequest();Object accessToken = request.getParameter("token");if(accessToken == null){logger.warn("token is empty");ctx.setSendZuulResponse(false);ctx.setResponseStatusCode(401);try {ctx.getResponse().getWriter().write("token is empty");} catch (IOException e) {return null;}}logger.info("ok");return null;}
}
測試
- 重啟 eureka-zuul-client
訪問 http://localhost:8051/hiapi/hi?name=victor
訪問 http://localhost:8051/hiapi/hi?name=victor&token=xx
完整代碼:GitHub
本人 C# 轉 Java 的 newbie, 如有錯誤或不足歡迎指正,謝謝