1. 程式人生 > >SpringCloud進擊 | 六淺出:服務閘道器 - 過濾器(Zuul Filter)【Finchley版本】

SpringCloud進擊 | 六淺出:服務閘道器 - 過濾器(Zuul Filter)【Finchley版本】


1.前言

上一節:SpringCloud進擊 | 五淺出:服務閘道器 - 路由(Zuul Router)【Finchley版本】

Zuul 本身是一系列過濾器的整合,那麼他當然也就提供了自定義過濾器的功能,Zuul 提供了四種過濾器:前置過濾器,路由過濾器,錯誤過濾器,簡單過濾器。實現起來也非常簡單,只需要編寫一個類去實現 Zuul 提供的介面 - ZuulFilter。

 

2.準備

延用上一節的專案,並分別啟動以下模組:

  • 服務註冊中心:wei-eureka-server,埠號:8090
  • 服務提供者:wei-service-provider,埠號:8010、8011
  • 服務消費者:wei-consumer-ribbon,埠號:8020

服務註冊中心(http://localhost:8090/)檢視服務,如下,註冊成功:

首先,可以正常訪問服務:http://localhost:8060/api/consumerGroup/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8011

如果不能正常訪問,可以看看上一節 SpringCloud進擊 | 六淺出:服務閘道器 - 路由(Zuul Router)【Finchley版本】

如上,準備工作完成。

 

3.進擊

3.1.pom.xml 依賴

檔案與上一節相同,無需改造

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wei</groupId>
    <artifactId>wei-gateway-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>wei-gateway-zuul</name>
    <description>Demo project for Spring Cloud Zuul</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

3.2.application.yml 配置檔案

檔案與上一節相同,無需改造

server:
  port: 8060    # 自定義程式啟動埠
spring:
  application:
    name: wei-gateway-zuul    # 指定進行服務註冊時該服務的名稱,服務與服務之間相互呼叫一般都是根據這個name
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定進行服務註冊的地址
zuul:
  routes:    # 配置路由對映
    wei-service-provider: /providerGroup/**    # 服務名稱對映。給指定的服務做對映,當前配置是將/wei-service-provider/**對映為/providerGroup/**
    consumerGroup:    # 保證唯一
      #url: http://localhost:8020/    # url繫結對映
      serviceId: wei-consumer-ribbon    # 給指定的服務做對映
      path: /consumerGroup/**    # path繫結對映。配置對映的路徑,當前配置是將/wei-consumer-ribbon/**對映為/consumerGroup/**
  prefix: /api
ribbon:
  eureka:
    enabled: false    # 在eureka中禁用 ribbon 的負載均衡
wei-consumer-ribbon:
  ribbon:    # 給配置serviceId對應的服務指定ribbon負載均衡,從listOfServers配置的服務地址中分配服務,多個用半形逗號分隔
    listOfServers: http://localhost:8010/, http://localhost:8011/

3.3.Zuul 過濾器

通過繼承 ZuulFilter 抽象類,編寫自定義 Filter。

package com.wei.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

@Component
public class WeiZuulFilter extends ZuulFilter {

    /**
     * 型別包含 pre、post、route、error
     * pre:在路由代理之前執行
     * route:代理的時候執行
     * error:出現錯的時候執行
     * post:在route 或者是 error 執行完成後執行
     *
     * 過濾器的型別,它決定過濾器在請求的哪個生命週期中執行
     * 這裡定義為pre,表示會在請求被路由之前執行
     *
     * @return 過濾器型別
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * Zuul filter 為鏈式過濾器,多個filter按順序執行,通過數字指定
     * 數字越大,優先順序越低
     *
     * @return 執行順序
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 是否啟用該過濾器
     * 判斷該過濾器是否需要被執行。這裡我們直接返回了true,因此該過濾器對所有請求都會生效
     * 實際運用中我們可以利用該函式來指定過濾器的有效範圍
     *
     * @return true:啟用過濾器 / false:禁用過濾器
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 過濾器的具體邏輯實現Demo
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {

        System.out.println("[ Demo For Zuul Filter ] Execute!");

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        String token = request.getParameter("token");

        if (StringUtils.isEmpty(token)) {
            System.out.println("[ Demo For Zuul Filter ] token parameter is empty!");

            // 使用 Zuul 過濾此種場景的請求,不對其進行路由
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(401);
            requestContext.setResponseBody("token is empty!");
        } else {
            System.out.println("[ Demo For Zuul Filter ] test success!");
        }

        return null;
    }
}

3.4.啟動類

檔案與上一節相同,無需要改造

package com.wei;

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

/**
 * 註解@EnableZuulProxy,開啟Zuul功能,自帶熔斷
 */
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class WeiGatewayZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(WeiGatewayZuulApplication.class, args);
    }
}

好了,到此,Zuul Filter 過濾器就已新增完成。其實只是增加並實現了一個自定義 ZuulFilter 類。

執行這個啟動類。

 

4.測試

訪問:http://localhost:8060/api/consumerGroup/demo/info?name=tester

瀏覽器輸出:

token is empty!

後臺日誌列印:

前端看一看,請求返回結果狀態是401。如此,說明 Zuul 過濾器已經在正常工作。

 

那我們把 token 引數加上:token=token_test

訪問:http://localhost:8060/api/consumerGroup/demo/info?name=tester&token=token_test

瀏覽器列印:

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010

後臺日誌列印:

有木有,通過過濾器,Zuul 正確路由到了配置檔案配置好的 加了字首(/api) 對映(/consumerGroup)的 /demo/info 介面。

 

5.總結

上面的程式碼實現過程中,當通過繼承 ZuulFilter 抽象類後,需要我們重寫它的四個方法:

  • filterType():過濾器的型別。它決定過濾器在請求的哪個生命週期中執行。其型別包含 pre、post、route、error。
  1. pre:在路由代理之前執行
  2. route:代理的時候執行
  3. error:出現錯的時候執行
  4. post:在route 或者是 error 執行完成後執行
  • filterOrder():過濾器的執行順序。當請求在一個階段中存在多個過濾器時,需要根據該方法返回的值來依次執行。通過數字指定,數字越大,優先順序越低。
  • shouldFilter():判斷該過濾器是否需要被執行。這裡我們直接返回了true,因此該過濾器對所有請求都會生效。實際運用中我們可以利用該函式來指定過濾器的有效範圍。
  • run():過濾器的具體邏輯。這裡我們只是簡單實現了一下,使用 Zuul 過濾入參沒有 token 場景的請求,不對其進行路由。

官方參考文件:https://springcloud.cc/spring-cloud-netflix.html


下一節,請繼續關注:SpringCloud進擊 | 一深入:高可用的服務註冊中心【Finchley版本】