網(wǎng)關挖掘機(一)Zuul網(wǎng)關1.x

參考github wiki:https://github.com/Netflix/zuul/wiki

一、什么是Zuul

Zuul是從設備和網(wǎng)站到Netflix流應用程序后端的所有請求的前門。作為邊緣服務應用程序,Zuul旨在實現(xiàn)動態(tài)路由,監(jiān)控,彈性和安全性。

特點

  • 身份驗證和安全性
  • 觀察與監(jiān)控
  • 動態(tài)路由
  • 壓力測試
  • 負載
  • 靜態(tài)響應處理
  • 多區(qū)域彈性

二、架構原理

  • 工作原理

    Zuul的中心是一系列的過濾器,能夠在請求和響應的路由過程中執(zhí)行一系列操作。

    Zuul提供了一個支持動態(tài)讀取、編譯和運行過濾器的框架。且過濾器之間不互相通信,而是通過RequestContext共享狀態(tài),且RequestContext 對于每個請求都是唯一的(ThreadLocal)。

  • 核心流程

all.jpg
  • 核心功能

    • 服務發(fā)現(xiàn)

      • 支持與 Eureka 無縫集成
      • 支持靜態(tài)服務類別以發(fā)現(xiàn)服務
    • 負載均衡

    • 連接池

    • 狀態(tài)類別

    • 重試

    • 申請護照

    • 申請重試

    • 原始并發(fā)保護

    • 相互TLS

    • 代理協(xié)議

    • GZip壓縮

  • 內(nèi)置Filter說明

    Zuul內(nèi)置了四種不同生命周期的過濾器類型,且過濾器之間不“直接”互相通信,而是通過RequestContext共享狀態(tài)。開發(fā)人員可以通過使用zuul來創(chuàng)建各種校驗規(guī)則的過濾器。

    Zuul RequestContext

    ? 為了在過濾器之間傳遞信息,Zuul使用了RequestContext。其數(shù)據(jù)保存在ThreadLocal每個請求的特定數(shù)據(jù)中

    過濾器類型如下

    • pre-filter(s):在請求被路由之前調用
    • route-filter(s):在路由請求時調用
    • error-filter(s):在處理請求發(fā)生錯誤時調用
    • post-filter(s):在route和error過濾器被調用之后調用

    Zuul請求的一個生命周期如下圖

filters.jpg

二、Zuul-Instance-Demo說明

1. 簡介

Demo只有服務注冊中心、網(wǎng)關服務、服務提供方

code_list.jpg

網(wǎng)關的配置

spring:
  application:
    name: zuul-api-gateway
  redis:
    cluster:
      nodes: 192.168.0.201:7000
      max-redirects: 3
    password: 123456
    jedis:
      pool:
        max-idle: 10
        max-active: 500
        max-wait: 1000


server:
  port: 1001

eureka:
  client:
    service-url:
      defaultZone: http://localhost:1001/eureka/

# 單實例配置:zuul.routes.<route>.path與zuul.routes.<route>.serviceId 參數(shù)對的方式配置
zuul:
#  # 過濾客戶端附帶的headers
#  sensitive-headers: Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Credentials,Access-Control-Allow-Headers,Access-Control-Expose-Headers,Access-Control-Max-Age
#  # 過濾網(wǎng)關內(nèi)服務之間通信所附帶的headers
#  ignored-headers: Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Credentials,Access-Control-Allow-Headers,Access-Control-Expose-Headers,Access-Control-Max-Age
  # 限流設置
  ratelimit:
    enabled: true
    # 配置60秒內(nèi)請求超3次,網(wǎng)關則拋異常,且60s后可恢復正常請求
    default-policy-list:
      - limit: 3
        quota: 2
        refresh-interval: 60
    repository: Redis
  # 路由設置
  routes:
    # 將對符合/api/** 規(guī)則的請求路徑轉發(fā)到服務名為eureka-provider的服務實例上
    service:
      path: /api/**
      serviceId: eureka-provider

    # 當訪問格式如:http://localhost:port/服務名/請求路徑 時,若遇到服務名太長,可如下做修改
    eureka-provider: /p/**

  # 前綴,所有服務調用需在方法路徑前加/api
#  prefix:/api

  # 排除服務
  ignored-services: eureka-provider1

# 設置超時時間,ribbon和hystrix能夠同時生效,且取兩者的最小值
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 4000



ribbon:
  # 該參數(shù)用來設置路由轉發(fā)請求的超時時間
  ReadTimeout: 4000
  # 該參數(shù)用來設置路由轉發(fā)請求的時候,創(chuàng)建請求連接的超時時間
  ConnectTimeout: 4000
  # 最大自動重試次數(shù)
  MaxAutoRetries: 1
  # 最大自動重試下一個服務的次數(shù)
  MaxAutoRetriesNextServer: 1
  eureka:
    enabled: true

2. 準備

  • 服務端口及參數(shù)定義

    啟動服務 端口 運行參數(shù)
    eureka-server 1000
    zuul-api-gateway 1001
    eureka-provider 1101 server.port=1101<br />thread.sleep-ms=500
    eureka-provider 1102 server.port=1102<br />thread.sleep-ms=1500
    eureka-provider 1103 server.port=1103<br />thread.sleep-ms=10000<br />eureka.instance.metadata-map.publish=gray

    注意:1103的實例配置的休眠時間是10秒,網(wǎng)關配置的超時時間是4秒必定會超時

  • 配置服務提供方

    以IDEA為例,打開Maven面板,依次打開eureka-provider——Plugins——spring-boot,右擊選擇spring-boot:run

    maven_config.jpg

依次添加三個provider實例,按以上表格的運行參數(shù)分別配置

provider_config.jpg

3. 運行

  • 啟動注冊中心
  • 啟動網(wǎng)關
  • 分別運行第二步配置的三個provider實例
  • 運行網(wǎng)關下的測試類TestZuul.java

三、使用開發(fā)

1. 引入依賴

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.5.RELEASE</version>
</parent>

<properties>
    <project.version>1.0.0</project.version>
    <cloud.version>2.1.2.RELEASE</cloud.version>
    <springboot.version>2.1.5.RELEASE</springboot.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-context</artifactId>
        <version>${cloud.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        <version>${cloud.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        <version>${cloud.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        <version>${cloud.version}</version>
    </dependency>

</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.RELEASE</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>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <encoding>UTF-8</encoding>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
    </plugins>
</build>

2. zuul配置

  • 步驟一:創(chuàng)建微服務網(wǎng)關:zuul-api-gateway

    這里默認已有eureka注冊中心eureka-server和服務提供方eureka-provider。

    服務提供方的某個controller

    @RestController
    @RequestMapping("/test/")
    public class TestController {
    
        @GetMapping("hello/{name}")
        public String hello(@PathVariable String name) throws InterruptedException {
            Thread.sleep(5* 1000);
            return "hello " + name;
        }
    }
    
  • 步驟二:配置文件

    spring:
      application:
        name: zuul-api-gateway
    
    server:
      port: 1000
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:1001/eureka/
    
    # 單實例配置:zuul.routes.<route>.path與zuul.routes.<route>.serviceId 參數(shù)對的方式配置
    zuul:
      routes:
      # 將對符合/api/** 規(guī)則的請求路徑轉發(fā)到服務名為eureka-provider的服務實例上
        service:
          path: /api/**
          serviceId: eureka-provider
        # 當訪問格式如:http://localhost:port/服務名/請求路徑 時,若遇到服務名太長,可如下做修改
        eureka-provider: /p/**
    
      # 前綴,所有服務調用需在方法路徑前加/api
    # prefix:/api
    
      # 排除服務
      ignored-services: eureka-provider1
    
  • 步驟三:添加啟動類

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

    這里解釋以下@EnableZuulServer@EnableZuulProxy的區(qū)別

    • @EnableZuulServer

      普通版Zuul Server, 支持基本的router和filter功能

      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Import({ZuulServerMarkerConfiguration.class})
      public @interface EnableZuulServer {
      }
      
    • @EnableZuulProxy

      增強版Zuul Server,在普通版的基礎上,結合eureka+ribbon+增加服務發(fā)現(xiàn)與熔斷等功能

      @EnableCircuitBreaker
      @Target({ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Import({ZuulProxyMarkerConfiguration.class})
      public @interface EnableZuulProxy {
      }
      

      詳細可查看ZuulProxyConfiguration

  • 步驟四:啟動

    訪問eureka注冊中心后臺:http://localhost:1001/,可以看到eureka上面注冊了服務提供方和zuul網(wǎng)關

    eureka.jpg

訪問方式:

正常即可轉發(fā)到服務提供方eureka-provider。

3. 設置超時時間

Zuul 內(nèi)部使用了 Ribbon 做負載均衡,它的默認超時時間是1s,當執(zhí)行一些比較長的請求,會被當做超時處理,返回504

timeout.jpg

在配置文件內(nèi)添加如下配置

# 設置超時時間,ribbon和hystrix能夠同時生效,且取兩者的最小值
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 30000
ribbon:
  ReadTimeout: 30000
  ConnectTimeout: 30000

4. 自定義熔斷降級策略

當Zuul中給定的路徑發(fā)生錯誤時,可以通過創(chuàng)建自定義FallbackProvider來提供熔斷響應,而Zuul默認的響應不太友好(比如上個point的超時錯誤返回504)。

  • 自定義熔斷處理類

    @Component
    public class TestFallbackProvider implements FallbackProvider {
    
        /**
         * 指定為哪個微服務提供回退功能,*表示所有微服務
         * @return
         */
        @Override
        public String getRoute() {
            return "eureka-provider";
        }
    
        /**
         * 返回體
         * @param route
         * @param cause
         * @return
         */
        @Override
        public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
            if (cause instanceof HystrixTimeoutException) {
                return response(HttpStatus.GATEWAY_TIMEOUT);
            } else {
                return response(HttpStatus.INTERNAL_SERVER_ERROR);
            }
        }
    
        private ClientHttpResponse response(final HttpStatus status) {
            return new ClientHttpResponse() {
                @Override
                public HttpStatus getStatusCode() throws IOException {
                    return status;
                }
    
                @Override
                public int getRawStatusCode() throws IOException {
                    return status.value();
                }
    
                @Override
                public String getStatusText() throws IOException {
                    return status.getReasonPhrase();
                }
    
                @Override
                public void close() {
                }
    
                @Override
                public InputStream getBody() throws IOException {
                    return new ByteArrayInputStream("服務暫時不可用,要不等一會再試試?".getBytes());
                }
    
                @Override
                public HttpHeaders getHeaders() {
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);
                    return headers;
                }
            };
        }
    }
    
    

5. 自定義Filter

通過繼承ZuulFilter,即可實現(xiàn)過濾器機制。

以下自定義過濾器用于校驗接口是否傳遞了token

@Slf4j
@Component
public class AccessFilter extends ZuulFilter {

    /**
     * 四種不同生命周期的過濾器類型
     * 1. pre:在請求被路由之前調用
     * 2. route:在路由請求時被調用
     * 3. post:在route和error過濾器之后被調用
     * 4. error:處理請求時發(fā)生錯誤時被調用·
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 過濾的優(yōu)先級,數(shù)字越大,優(yōu)先級越低。
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * @return 該過濾器是否需要被執(zhí)行
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());

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

        if(Objects.isNull(token)) {
            log.warn("token is empty");
            // 讓zuul過濾該請求,不對其進行路由
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            return null;
        }
        log.info("token is ok");
        return null;
    }
}

訪問方式:

正常即可轉發(fā)到服務提供方eureka-provider。

6. 限流

  • 引入依賴包spring-cloud-zuul-ratelimit,支持與zuul整合提供分布式限流策略、

    github地址:https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit

    <dependency>
        <groupId>com.marcosbarbero.cloud</groupId>
        <artifactId>spring-cloud-zuul-ratelimit</artifactId>
        <version>2.2.4.RELEASE</version>
    </dependency>
    
    
    • 限流粒度:

      粗粒度:

      • 網(wǎng)關限流
      • 單個服務限流

      細粒度:

      • url:對請求的目標url進行限流
      • origin:對請求來源ip進行限流
      • user:對特定用戶(比如系統(tǒng)的非vip用戶)進行限流
      • serviceId:對特定服務id進行限流
    • 限流統(tǒng)計數(shù)據(jù)存儲

      ConsulRateLimiter:Consul

      RedisRateLimiter:Redis

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      
      

      SpringDataRateLimiter:Spring Data

      Bucket4jJCacheRateLimiter:Bucket4j

      Bucket4jHazelcastRateLimiter:Bucket4j

      Bucket4jIgniteRateLimiter: Bucket4j

      Bucket4jInfinispanRateLimiter:Bucket4j

    • 限流配置模板

      zuul:
        ratelimit:
          key-prefix: your-prefix
          # 開啟限流
          enabled: true
          # 存儲類型,用于存儲統(tǒng)計信息(對于不同的存儲類型,需要在pom添加不同的依賴)
          repository: REDIS
          behind-proxy: true
          add-response-headers: true
          
          default-policy-list: #optional  全局配置
            - limit: 10 #optional - 單位時間內(nèi)窗口的請求數(shù)限制
              quota: 1000 #optional - 單位時間內(nèi)窗口的請求總時間限制
              refresh-interval: 60 # 單位時間設置
              type: #optional
                - user        # 通過登錄用戶區(qū)分
                - origin      # 通過請求ip區(qū)分
                - url         # 通過請求路徑區(qū)分
                - httpmethod  # 通過請求類型區(qū)分
          
          #################################################################
              
          policy-list: # 局部配置(對特定的服務id進行限流)
            myServiceId:
              - limit: 10 #optional - request number limit per refresh interval window
                quota: 1000 #optional - request time limit per refresh interval window (in seconds)
                refresh-interval: 60 #default value (in seconds)
                type: #optional
                  - user
                  - origin
                  - url
              - type: #optional value for each type
                  - user=anonymous
                  - origin=somemachine.com
                  - url=/api #url prefix
                  - role=user
                  - httpmethod=get #case insensitive
      
      
  • 測試

    這里選用redis作為網(wǎng)關的數(shù)據(jù)存儲,需要引入redis的依賴包,并且配置redis和ratelimit

    spring:
      application:
        name: zuul-api-gateway
      redis:
        cluster:
          nodes: 192.168.0.201:7000
          max-redirects: 3
        password: 123456
        jedis:
          pool:
            max-idle: 10
            max-active: 500
            max-wait: 1000
            
    zuul:
      # 開啟全局配置限流
      ratelimit:
        enabled: true
        # 配置60秒內(nèi)請求超3次,網(wǎng)關則拋異常,且60s后可恢復正常請求
        default-policy-list:
          - limit: 3
            quota: 2
            refresh-interval: 60
        repository: Redis
    
    

    結果如下:

    ratelimit.png

4. 負載均衡

5. 路由重試

引入依賴

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

配置 zuul.retryable=true

zuul-demo-provider:   #指定服務
  ribbon:
    MaxAutoRetries: 0    #本服務重試次數(shù)
    MaxAutoRetriesNextServer: 1   #重試下一個服務個數(shù)
    ReadTimeout: 1000     
    ConnectTimeout: 3000

6. 權限集成

參考自定義Filter的實現(xiàn)方式。

7. 灰度發(fā)布

8. 開啟跨域

注意:當網(wǎng)關配置了跨域處理后,內(nèi)部服務則不需要配置

配置過濾請求頭

zuul:
  # 過濾客戶端附帶的headers
  sensitive-headers: Access-Control-Allow-Origin
  # 過濾網(wǎng)關內(nèi)服務之間通信所附帶的headers
  ignored-headers: Access-Control-Allow-Origin

配置解決跨域訪問問題

@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);       // 允許cookies跨域
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.setMaxAge(18000L);
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

感謝閱讀,Ending...

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • (git上的源碼:https://gitee.com/rain7564/spring_microservices_...
    sprainkle閱讀 17,242評論 3 32
  • API網(wǎng)關服務 Spring Cloud Zuul API網(wǎng)關是一個更為智能的應用服務器,它的存在就像是整個微服務...
    jeff_chang_liu閱讀 316評論 0 2
  • 這次去高九期的神秘之旅,這是我第四次操辦神秘之旅。相比剛開始的茫然、焦慮、慌亂、緊張,時時刻刻都發(fā)現(xiàn)還有很多細節(jié)沒...
    笨蝸牛閱讀 465評論 0 2
  • 1、如果身心無力的時候,一定拿出自己的拳頭,狠狠的砸自己,要砸出力氣。 2、讓你的思維從繁化簡,找到你現(xiàn)在的一個小...
    耿艷菊閱讀 226評論 2 2

友情鏈接更多精彩內(nèi)容