1. 程式人生 > 實用技巧 >SpringCloud入門實戰(7)-Zuul使用

SpringCloud入門實戰(7)-Zuul使用

Zuul是Netflix開源的微服務閘道器,主要功能是路由轉發和過濾器,並和Ribbon結合實現了負載均衡的功能。本文主要介紹Zuul的基本使用,文中使用到的軟體版本:Spring Boot 2.2.5.RELEASE、Spring Cloud Hoxton.SR3、Java 1.8.0_191。

1、Zuul功能架構

zuul的核心是一系列的filters, 其作用可以類比Servlet框架的Filter,或者AOP。

2、過濾器

2.1、過濾器生命週期

2.2、過濾器型別

(1)PRE:這種過濾器在請求被路由之前呼叫,可利用這種過濾器實現身份驗證、在叢集中選擇請求的微服務、記錄除錯資訊等。

(2)ROUTING:這種過濾器將請求路由到微服務,這種過濾器用於構建傳送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
(3)POST:這種過濾器在路由到微服務以後執行,這種過濾器可用來為響應新增標準的HTTP Header、收集統計資訊和指標、將響應從微服務傳送給客戶端等。
(4)ERROR:在其他階段發生錯誤時執行該過濾器。

2.3、預設過濾器

2.3.1、pre過濾器

ServletDetectionFilter:用來檢測當前請求是通過Spring的DispatcherServlet處理執行的,還是通過ZuulServlet來處理執行的
Servlet30WrapperFilter:將原始的HttpServletRequest包裝成Servlet30RequestWrapper物件

FormBodyWrapperFilter:將符合要求的請求體包裝成FormBodyRequestWrapper物件
DebugFilter:將當前請求上下文中的debugRouting和debugRequest引數設定為true
PreDecorationFilter:判斷當前請求上下文中是否存在forward.do和serviceId引數,如果都不存在,那麼它就會執行具體過濾器的操作

2.3.2、route過濾器

route過濾器:對請求上下文中存在serviceId引數的請求進行處理,即只對通過serviceId配置路由規則的請求生效
SimpleHostRoutingFilter:該過濾器只對請求上下文存在routeHost引數的請求進行處理,即只對通過url配置路由規則的請求生效

SendForwardFilter:該過濾器只對請求上下文中存在的forward.do引數進行處理請求,即用來處理路由規則中的forward本地跳轉

2.3.3、post過濾器

SendErrorFilter:該過濾器利用上下文中的錯誤資訊來組成一個forward到api閘道器/error錯誤端點的請求來產生錯誤響應
SendResponseFilter:該過濾器的處理邏輯就是利用上下文的響應資訊來組織需要傳送回客戶端的響應內容

3、Zuul功能

認證和安全:識別每個需要認證的資源,拒絕不符合要求的請求
效能監測:在服務邊界追蹤並統計資料,提供精確的生產檢視
動態路由:根據需要將請求動態路由到後端叢集
壓力測試:逐漸增加對叢集的流量以瞭解其效能
Load Shedding:為每種型別的請求分配容量並刪除超過限制的請求
靜態資源處理:直接在邊緣構建一些響應,而不是將它們轉發到內部叢集

4、使用

4.1、引入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

4.2、啟動類

package com.inspur.scdemo.zuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }
}

4.3、配置zuul

zuul:
  ignored-patterns: /**/test/** #忽略所有包含/test/的地址請求
  ignored-services: '*' #'*',忽略的所有服務;如果不配置該項,則預設為所有服務生成路由:http://zull-ip:zull-port/serviceId/**
  routes:
    scdemo-server: /scdemo-server/**  #路由到scdemo-server服務
    scdemo-server-url:
      path: /scdemo-server-url/**
      url: http://localhost:9001/     #直接url指定地址
    scdemo-server-forward:
      path: /scdemo-server-forward/**
      url: forward:/test             #跳轉到http://zull-ip:zull-port/test

假設zuul的埠為9007,上面配置了三種路由方式:

a、路由到服務 http://localhost:9007/scdemo-server/user/getUser?id=1將路由到http://scdemo-server-host:scdemo-server-port//user/getUser?id=1
b、路由到url 這些簡單的url路由不會作為HystrixCommand來執行,也不會使用Ribbon對多個URL進行負載均衡。http://localhost:9007/scdemo-server-url/user/getUser?id=1將路由到http://localhost:9001/user/getUser?id=1
c、forward http://localhost:9007/scdemo-server-forward/user/getUser?id=1將路由到http://localhost:9007/test/user/getUser?id=1

4.4、自定義過濾器

如果前端發起請求沒有帶token將允許請求

package com.inspur.scdemo.zuul.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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class GlobalFilter extends ZuulFilter {
    private Logger logger = LoggerFactory.getLogger(GlobalFilter.class);

    /**設定過濾型別*/
    @Override
    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }

    /**設定過過濾器優先順序*/
    @Override
    public int filterOrder() {
        return 0;
    }

    /**設定過過濾器優先順序*/
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String token = request.getHeader("token");
        //TODO:己驗token是否正確
        if (StringUtils.isEmpty(token)) {
            //返回錯誤資訊
            context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            context.setResponseBody(HttpStatus.UNAUTHORIZED.getReasonPhrase());
            context.setSendZuulResponse(false);
        }
        return null;
    }
}