1. 程式人生 > 程式設計 >服務閘道器Spring Cloud Zuul

服務閘道器Spring Cloud Zuul

Spring Cloud Zuul

開發環境

  • idea 2019.1.2
  • jdk1.8.0_201
  • Spring Boot 2.1.9.RELEASE
  • Spring Cloud Greenwich SR3

Zuul介紹

Zuul是Netflix開發的一款提供動態路由、監控、彈性、安全的閘道器服務,他可以和Eureka,Ribbon,Hystrix等元件配合使用。還可以通過建立過濾器對校驗過濾提供支援,使微服務應用更專注於業務邏輯的開發。

使用Zuul閘道器服務帶來的好處是統一向外系統提供REST API,並額外提供了許可權控制、負載均衡等功能,並且這些功能是從原先的服務中抽離出來並單獨存在的。

Zuul提供了不同型別的filter用於處理請求,這些filter可以讓我們實現以下功能

  • 許可權控制和安全性:可以識別認證需要的資訊和拒絕不滿足條件的請求
  • 監控:監控請求資訊
  • 動態路由:根據需要動態地路由請求到後臺的不同服務叢集
  • 壓力測試:逐漸增大到叢集的流量,以便進行效能評估
  • 負載均衡:為每種型別的請求分配容量並丟棄超過限額的請求
  • 限流
  • 黑白名單過濾
  • 靜態資源處理:直接在zuul處理靜態資源的響應而不需要轉發這些請求到內部叢集中

過濾器

ZuulFilter是一個基礎的抽象類,定義了一些抽象方法

  • filterType方法: filter的型別,有”pre”,“route”,“post”,“error”,“static”

    • pre:在請求被路由之前執行
    • route:在請求被路由時執行
    • post:在請求被路由之後執行
    • error:在請求發生錯誤時執行
    • static:特殊的 Filter 具體的可以看 StaticResponseFilter,它允許從 Zuul 本身生成響應,而不是將請求轉發到源
  • filterOrder方法:優先順序,級別越高,越快被執行(數值越小表示級別越高)

  • shouldFilter方法:開關,如果是true,run方法會執行,否則不會執行

  • run方法:filter執行的邏輯操作

程式碼實現

1.建立服務註冊中心

建立 zuul-eureka-server 專案,引入eureka-server依賴,專案完整原始碼可以檢視:

Spring Cloud Zuul 示例原始碼

以下貼幾段關鍵程式碼

pom新增依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
複製程式碼

啟動類增加 @EnableEurekaServer 註解

@EnableEurekaServer
@SpringBootApplication
public class ZuulEurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulEurekaServerApplication.class,args);
    }
}
複製程式碼

yml配置

server:
  port: 8761

spring:
  application:
    name: zuul-eureka-server

eureka:
  instance:
    hostname: localhost   # eureka 例項名稱
  client:
    register-with-eureka: false # 不向註冊中心註冊自己
    fetch-registry: false       # 是否檢索服務
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/  # 註冊中心訪問地址
複製程式碼

2.建立服務提供者1

建立 zuul-server-provider 專案,引入eureka-client依賴,專案完整原始碼可以檢視:Spring Cloud Zuul 示例原始碼

以下貼幾段關鍵程式碼

pom新增依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
複製程式碼

編寫HelloController服務

@RestController
@Slf4j
public class HelloController {

    @RequestMapping("/hello")
    public String index(@RequestParam String name) {
        log.info("request one  name is " + name);
        return "hello " + name + ",this is first messge";
    }
}
複製程式碼

啟動類增加 @EnableDiscoveryClient 註解

@SpringBootApplication
@EnableDiscoveryClient
public class ZuulServerProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulServerProviderApplication.class,args);
    }
}
複製程式碼

yml配置

spring:
  application:
    name: zuul-server-provider
server:
  port: 9000
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
複製程式碼

3.建立服務提供者2

建立 zuul-server-provider2 專案,引入eureka-client依賴,其它同服務提供者1專案,專案完整原始碼可以檢視:Spring Cloud Zuul 示例原始碼

以下貼出差異部分程式碼

編寫服務,這裡為了做服務降級測試,為當前執行緒設定了一個超長休眠時間

@RestController
@Slf4j
public class HelloController {
    @RequestMapping("/hello")
    public String index(@RequestParam String name) {
        log.info("request two name is " + name);
        try{
            //為做服務降級測試,設定一個超長休眠時間,故意導致該服務訪問超時
            Thread.sleep(1000000);  
        }catch ( Exception e){
            log.error(" hello two error",e);
        }
        return "hello " + name + ",this is two messge";
    }
}
複製程式碼

4.建立zuul服務閘道器

建立 zuul-server-gateway 專案,引入netflix-zuul及eureka-client依賴,專案完整原始碼可以檢視:Spring Cloud Zuul 示例原始碼

以下貼幾段關鍵程式碼

pom.xml配置

        <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>
複製程式碼

建立過濾器TokenFilter.java

package com.easy.zuulServerGateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import javax.servlet.http.HttpServletRequest;

@Slf4j
public class TokenFilter extends ZuulFilter {

    @Override
    public String filterType() {
        //可以在請求被路由之前呼叫
        return "pre";
    }

    @Override
    public int filterOrder() {
        //filter執行順序,通過數字指定,優先順序為0,數字越大,優先順序越低
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        //是否執行該過濾器,此處為true,說明需要過濾
        return true;
    }

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

        log.info("--->>> TokenFilter {},{}",request.getMethod(),request.getRequestURL().toString());

        //獲取請求的引數
        String token = request.getParameter("token");

        if (StringUtils.isNotBlank(token)) {
            //對請求進行路由
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess",true);
            return null;
        } else {
            //不對其進行路由
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("token is empty");
            ctx.set("isSuccess",false);
            return null;
        }
    }
}
複製程式碼

建立 zuul-server-provider 服務對應的熔斷器(這裡針對整個服務熔斷,也可以對單個服務介面做熔斷處理),ProviderFallback.java

package com.easy.zuulServerGateway.fallback;

import lombok.extern.slf4j.Slf4j;
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.InputStream;

@Slf4j
@Component
public class ProviderFallback implements FallbackProvider {

    @Override
    public String getRoute() {
        return "zuul-server-provider";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route,Throwable cause) {
        if (cause != null) {
            String reason =cause.getMessage();
            log.info("Excption {}",reason);
        }
        return fallbackResponse();
    }

    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() {
                return HttpStatus.OK;
            }

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

            @Override
            public String getStatusText(){
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() {
                return new ByteArrayInputStream("The service is unavailable.".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}
複製程式碼

yml配置

spring:
  application:
    name: zuul-service-gateway
server:
  port: 8888

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

  #是否開啟重試功能
zuul:
  retryable: true
  #對當前服務的重試次數
ribbon:
  MaxAutoRetries: 2
  #切換相同Server的次數
  MaxAutoRetriesNextServer: 0
複製程式碼

啟動類增加 @EnableZuulProxy 註解,來啟動服務閘道器 ZuulServerGatewayApplication.java

package com.easy.zuulServerGateway;

import com.easy.zuulServerGateway.filter.TokenFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableZuulProxy
public class ZuulServerGatewayApplication {

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

    @Bean
    public TokenFilter tokenFilter() {
        return new TokenFilter();
    }
}
複製程式碼

至上,示例的四個服務建立完畢,接下來執行示例檢視效果

使用

現有四個專案如下

zuul-eureka-server:服務註冊中心,服務名:zuul-eureka-server,埠:8761 zuul-server-provider:服務提供者1,服務名:zuul-server-provider,埠:9000 zuul-server-provider2:服務提供者,服務名:zuul-server-provider,埠:9001 zuul-server-gateway:服務閘道器,服務名:zuul-server-gateway,埠:8888

執行測試

分別啟動zuul-eureka-server、zuul-server-gateway、zuul-server-provider三個服務

啟動zuul-server-provider2

  • 多次訪問http://localhost:8888/zuul-server-provider/hello?name=yuntian&token=xx,此時會交替返回
hello yuntian,this is first messge
The service is unavailable
...

複製程式碼

從返回結果可以看出:zuul-server-provider2專案已經啟用了熔斷,返回:The service is unavailable.

資料