springcloud——zuul

Zuul默認使用的HTTP客戶端是Apache HTTPClient,主要功能是路由轉發過濾器。zuul的核心是一系列的filters, 類似於java Servlet框架的Filter,或者AOP。zuul把請求路由到用戶處理邏輯的過程中,這些filter參與一些過濾處理,比如Authentication;外圍系統或者用戶通過網關訪問服務,網關通過註冊中心找到對應提供服務的客戶端(Ribbon獲取服務),網關也需要到註冊中心進行註冊。

Zuul可以爲同一註冊中心中的其他服務的訪問做反向代理(配置serviceId屬性),也可以爲外部服務訪問做代理(配置URL屬性),還可以對zuul服務本身的接口做代理(配置URL屬性的值爲forward關鍵字加路徑)

搭建Zuul網關服務實現路由和過濾

1.引入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>

 2.在啓動類上添加@EnableZuulProxy註解


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

3.自定義配置zuul服務的路由 (zuul默認以服務ID作爲路由配置)

zuul默認情況下會以註冊中心中的服務id作爲路由設置,即如果請求是註冊中心中的某個serviceId組成的URL就路由給對應的服務;zuul中的服務列表使用的是Ribbon實現的;

瞭解下zuul節點下的配置屬性

      zuul.prefix:api          配置在zuul節點下的屬性,值爲api 表示爲所有URL加一個前綴

      zuul.ignoredServices:"*"       表示禁用默認的路由配置,值爲*表示禁用所有默認的路由,出去下面routes節點下配置的            路由外其他都無效。此屬性也可以禁用特定的某個或多個默認的服務ID路由,多個服務ID用逗號隔開。

      zuul.routes:       這個節點是路由配置開始的節點,其下可以定義多個配置組

      zuul.routes.path:  指的是用戶訪問的URL

      zuul.routes.serviceId  / zuul.routes.url:   路由目標服務在Eureka中的服務實例ID或者服務的url地址

在目標服務集成Eureka的情況下自定義路由(目標服務使用serviceId配置)

routes節點後配置路由,,。

當然,目標服務也可以使用URL代替serviceId,只不過這種情況下要實現負載訪問就要禁用Eureka的Ribbon,然以單獨配置Ribbon並執行負載的多個服務ID,這種方式感覺並沒有什麼用。。。。

如下示例配置了兩組路由,一組是customerURL,第二組是userURL。配置裏有prefix前綴屬性爲api,所以第一組配置就是把用戶訪問地址以api/customer/*的請求路由到customer-service這個服務中;第二組配置就是把api/user/*的請求路由到user-service這個服務中。

server:
  port: 8040
spring:
  application:
    name: zuul
# 配置Eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
# 構建路由地址
zuul:
    #所有URL都要加api這個前綴
  prefix:api          
  # 禁用默認的路由設置,使用此配置以後只有下面配置的兩組路由纔會生效
  ignoredServices:"*"
  routes:
    # 這裏可以自定義,只是一個唯一的標識
    customerURL:
      # 匹配的路由規則
      path: /customer/**
      # 路由的目標服務名
      serviceId: customer-service
    userURL:
      # 匹配的路由規則
      path: /user/**
      # 路由的目標服務名
      serviceId: user-service

上面這種配置中,如果某個服務有多個實例,以Eureka中集成Ribbon負載方式進行訪問;如果使用URL配置目標服務的路由,就要單獨配置Ribbon來實現負載:

server:
  port: 8080
spring:
  application:
    name: zuul
# 配置eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
# 構建路由地址
zuul:
  routes:
    # 這裏可以自定義
    demo2:
      # 匹配的路由規則
      path: /demo/**
      # 使用URL配置,不適用服務ID
      url: http://127.0.0.1:8070
# 關閉使用eureka負載路由
ribbon:
  eureka:
    enabled: false
# 如果不使用eureka的話,需要自己定義路由的那個服務的其他負載服務
demo:
  ribbon:
    # 這裏寫你要路由的demo服務的所有負載服務請求地址,本項目只啓動一個,因此只寫一個
    listOfServers: http://localhost:8090/

對於path屬性的配置,要符合ant樣式:

            user/*         這個配置只對user/id這種/後一個值得url生效

             user/**      這個配置可以以user/開頭,任意個值得url生效。 

不使用Eureka的情況下自定義配置路由(配置URL屬性代替serviceId,無負載均衡)

由於沒有Eureka註冊中心,也就沒有服務ID,只能使用URL地址配置服務的路由地址。

server:
  port: 8080
spring:
  application:
    name: zuul
# 構建路由地址
zuul:
  routes:
    # 這裏可以自定義
    demo2:
      # 匹配的路由規則
      path: /demo/**
      # 路由的目標地址
      url: http://localhost:8090/

4.Zuul過濾器配置 

 默認情況下,Spring Cloud Zuul在請求路由時,會過濾掉請求頭信息中的 一些敏感信息,防止它們被傳遞到下游的外部服務器。

zuul定義了四種標準過濾器類型,這四種過濾器分別對應着請求的生命週期

  • pre:路由請求前過濾。

  • post:路由請求後(此時已經走完目標服務程序了)過濾。

  • route:路由請求時過濾。

  • error:當上述三種過濾器拋出異常時,會走error過濾。

自定義並使用Zuul Filter

自定義過濾器很簡單,只要繼承ZuulFilter類並重寫相應的方法,用@Component註解作爲spring的bean使用即可。

ZuulFilter類提供瞭如下方法方便我們自定義和實現一個Zuul過濾器:

    public String filterType():設置這個過濾器的類型是

    public int filterOrder() :設置過濾器的優先級,多個同類型(filterType)ZuulFilter中哪個filter的此方法返回值最小優先級最高

    public boolean shouldFilter() :決定此過濾器是否生效,返回true表示生效
 
    public Object run() :過濾器的邏輯執行方法,在這個方法內調用doFiltrate方法

    private boolean doFiltrate (HttpServletRequest request) :過濾邏輯,返回是否通過過濾(true或false)

下面是自定義一個pre類型的ZuulFilter的示例代碼:

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.util.RequestBodyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
 
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Random;
 

@Component
public class PreFilter extends ZuulFilter {
 
    /**
     * 定義過濾類型
     *  【pre】路由請求前被調用過濾、
     *  【post】後置過濾、
     *  【error】錯誤過濾、
     *  【route】路由請求時被調用
     */
    @Override
    public String filterType() {
        // 設置這個過濾器的類型是 前置過濾器
        return "pre";
    }
 
    /**
     * 設置過濾器的優先級,如果服務中有多個同類型(filterType)的ZuulFilter
     *
     * 這個方法的值越小代表越先過濾
     */
    @Override
    public int filterOrder() {
        return 0;
    }
 
    /**
     * 決定此過濾器是否生效,返回true表示生效
     */
    @Override
    public boolean shouldFilter() {
       return true;
    }
 
    /**
     * 過濾器的邏輯執行方法,在這個方法內調用doFiltrate方法
     *
     * 注:當RequestContext.setSendZuulResponse(false);時表示過濾失敗,zuul不對其進行路由
     */
    @Override
    public Object run() {
        // 獲取請求上下文
        RequestContext requestContext = RequestContext.getCurrentContext();
 
        // 獲取請求
        HttpServletRequest request = requestContext.getRequest();
        if (doFiltrate(request)) {
            // 驗證通過
            return null;
        }
        // 如果驗證不通過,那麼過濾該請求,不往下級服務去轉發請求,到此結束
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
        requestContext.setResponseBody(HttpStatus.FORBIDDEN.getReasonPhrase());
        requestContext.getResponse().setContentType("text/html;charset=UTF-8");
        return null;
    }
    
    /**
     * 執行過濾邏輯,返回是否通過過濾結果
     */
    private boolean doFiltrate (HttpServletRequest request) {
        try {
            /// 獲取請求頭
            String who = request.getHeader("Authorization");
            log.info(" requestHeader param 【Authorization】 is -> {} !", who);
 
            /// 向請求頭中添加信息
            // requestContext.addZuulRequestHeader("");
 
            // 獲取請求體
            RequestBodyUtil requestBodyUtil = new RequestBodyUtil(request);
            String requestBody = requestBodyUtil.getBody();
            log.info(" got requestBady -> {}", requestBody);
 
            // TODO 由於是測試代碼,這裏隨機返回 成功、失敗
            return new Random().nextBoolean();
        } catch (Exception e) {
            log.error(" zull authe occur error !", e);
            return false;
        }
    }
}

禁用Zuul中默認啓用的過濾器 

上面提到了我們自己定義的filter如果不需要使用可以通過shouldFilter方法返回false實現對這個過濾器是禁用。但是Zuul默認啓用了一些filter的bean,如果我們不想使用顯然沒辦法通過修改方法來實現,這個時候我們可以通過配置yml來實現:

直接在配置文件中:zuul.<filtername>.<filtertype>.disable=true

例如:

zuul:
  DebugFilter:
    pre:
      disable: true

 

 

 

 

 

 

 

 

 

 

 

 

發佈了137 篇原創文章 · 獲贊 36 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章