SpringCloud學習之Zuul路由轉發、攔截和熔斷處理(七)


Spring Cloud Zuul

服務網關是微服務架構中一個不可或缺的部分。通過服務網關統一向外系統提供REST API的過程中,除了具備服務路由、均衡負載功能之外,它還具備了權限控制等功能。

Spring Cloud Netflix中的Zuul就擔任了這樣的一個角色,為微服務架構提供了前門保護的作用,同時將權限控制這些較重的非業務邏輯內容遷移到服務路由層面,使得服務集群主體能夠具備更高的可復用性和可測試性。

在Spring Cloud體系中, Spring Cloud Zuul 封裝了Zuul組件,作為一個API網關,負責提供負載均衡、反向代理和權限認證。

Zuul工作機制

過濾器機制

Zuul的核心是一系列的filters, 其作用類似Servlet框架的Filter,Zuul把客戶端請求路由到業務處理邏輯的過程中,這些filter在路由的特定時期參與了一些過濾處理,比如實現鑒權、流量轉發、請求統計等功能。Zuul的整個運行機制,可以用下圖來描述。

過濾器的生命周期

Filter的生命周期有4個,分別是“PRE”、“ROUTING”、“POST”、“ERROR”,整個生命周期可以用下圖來表示。

基於Zuul的這些過濾器,可以實現各種豐富的功能,而這些過濾器類型則對應於請求的典型生命周期。

PRE: 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集群中選擇請求的微服務、記錄調試信息等。

ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建發送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。

POST:這種過濾器在路由到微服務以后執行。這種過濾器可用來為響應添加標准的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。

ERROR:在其他階段發生錯誤時執行該過濾器。

除了默認的過濾器類型,Zuul還允許我們創建自定義的過濾器類型。例如,我們可以定制一種STATIC類型的過濾器,直接在Zuul中生成響應,而不將請求轉發到后端的微服務。

Zuul中默認實現的Filter

Zuul默認實現了很多Filter,這些Filter如下面表格所示。

類型 順序 過濾器 功能
pre -3 ServletDetectionFilter 標記處理Servlet的類型
pre -2 Servlet30WrapperFilter 包裝HttpServletRequest請求
pre -1 FormBodyWrapperFilter 包裝請求體
route 1 DebugFilter 標記調試標志
route 5 PreDecorationFilter 處理請求上下文供后續使用
route 10 RibbonRoutingFilter serviceId請求轉發
route 100 SimpleHostRoutingFilter url請求轉發
route 500 SendForwardFilter forward請求轉發
post 0 SendErrorFilter 處理有錯誤的請求響應
post 1000 SendResponseFilter 處理正常的請求響應

 

我們先看看Zuul的工作原理,Zuul是主要起路由轉發和路由攔截作用的,這里直接說Zuul和Feign集成的工作流程如下:

 

新建一個Consumer服務消費模塊,他的pom.xml添加如下依賴:

看看他的主程序:

再看看application.yml里的配置文件:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
      prefer-ip-address: true
server:
  port: 8768
spring:
  application:
    name: service-ribbon
zuul:
  routes:
    #標識你服務的名字,這里可以自己定義,一般方便和規范來講還是跟自己服務的名字一樣
    service-feign:
      #服務映射的路徑,通過這路徑就可以從外部訪問你的服務了,目的是為了不爆露你機器的IP,面向服務的路由了,給你選一個可用的出來,
      path: /service-feign/**
      serviceId: service-feign

啟動我們的注冊中心服務、至少一個服務提供者、啟動Feign服務消費者、啟動我們的Zuul服務消費者,啟動正常:

打開瀏覽器器,輸入http://localhost:8768/service-feign/hello?name=111這是我們基於Feign的請求轉發

打開瀏覽器器,輸入http://localhost:8768/service-feign/hi?name=111這是我們基於RestTemplate的請求轉發

可以看到,我們成功將我們的路由請求轉發到Feign服務消費者模塊上了,接下來我們要實現以下Zuul對請求的攔截和熔斷處理,

攔截就是在轉發之前或者之后當請求到達Feign之前做攔截處理,這個是很重要的功能,熔斷是當我們路由轉發請求的服務不可用該怎么處理響應呢?

一、路由請求攔截

package com.xu.serviceconsumer.component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class MyTokenFilter extends ZuulFilter {

    private Logger logger = LoggerFactory.getLogger(MyTokenFilter.class);

    //四種類型:pre,routing,error,post
    //pre:主要用在路由映射的階段是尋找路由映射表的
    //routing:具體的路由轉發過濾器是在routing路由器,具體的請求轉發的時候會調用
    //error:一旦前面的過濾器出錯了,會調用error過濾器。
    //post:當routing,error運行完后才會調用該過濾器,是在最后階段的
    @Override
    public String filterType() {
        return "pre";
    }

    //自定義過濾器執行的順序,數值越大越靠后執行,越小就越先執行
    @Override
    public int filterOrder() {
        return 1;
    }

    //控制過濾器生效不生效,可以在里面寫一串邏輯來控制
    @Override
    public boolean shouldFilter() {
        return true;
    }

    //執行過濾邏輯
    @Override
    public Object run() {

        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String url = request.getRequestURL().toString();
        logger.info("請求URL:"+url);
        String token = request.getParameter("name");
        if (token == null){
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            context.setResponseBody("there is no request parameter name");


            return null;
        }
        return null;
    }
}

簡單看一下代碼知道這個過濾器繼承了ZuulFilter,在run()方法里日志輸出了請求的URL路徑和對參數是否有name做了簡單的攔截過濾,請求http://localhost:8768/service-feign/hello?

可以看到這個Zuul的路由過濾器成功的對這個請求攔截過濾了,最后看一下控制台信息也可以看到成功輸出打印了請求的URL

 

二、路由熔斷

package com.xu.serviceconsumer.component;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class MyFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        return "service-feign";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        System.out.println("route:"+route);
        System.out.println("exception:"+cause.getMessage());
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "ok";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream(("Sorry, the service "+getRoute()+" is unavailable now.").getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

注意這里的一段代碼

我們的熔斷路由器配置的是service-feign(就是我們zuul請求路由的這個這個服務),我們關閉這個服務,保留其他之前開啟的服務,請求http://localhost:8768/service-feign/hi?name=111

自此,我們可以

自此,說明我們自定義的熔斷器已經起作用了,這課的內容到此為止,下回再見!

===============================================================================

如果您覺得此文有幫助,可以打賞點錢給我支付寶或掃描二維碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM