服務網關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等組件配合使用。還能夠經過建立過濾器對校驗過濾提供支持,使微服務應用更專一於業務邏輯的開發。java

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

Zuul提供了不一樣類型的filter用於處理請求,這些filter可讓咱們實現如下功能github

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

過濾器

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

  • filterType方法: filter的類型,有」pre」, 「route」, 「post」, 「error」, 「static」
    • pre:在請求被路由以前執行
    • route:在請求被路由時執行
    • post:在請求被路由以後執行
    • error:在請求發生錯誤時執行
    • static:特殊的 Filter 具體的能夠看 StaticResponseFilter,它容許從 Zuul 自己生成響應,而不是將請求轉發到源
  • filterOrder方法:優先級,級別越高,越快被執行(數值越小表示級別越高)apache

  • shouldFilter方法:開關,若是是true,run方法會執行,不然不會執行安全

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

代碼實現

1.建立服務註冊中心

建立 zuul-eureka-server 項目,引入eureka-server依賴,項目完整源碼能夠查看:Spring Cloud Zuul 示例源碼負載均衡

如下貼幾段關鍵代碼ide

pom添加依賴spring-boot

<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三個服務

  • 訪問地址:http://localhost:8888/zuul-server-provider/hello?name=yuntian,返回:token is empty ,請求被攔截返回。
  • 訪問地址:http://localhost:8888/zuul-server-provider/hello?name=yuntian&token=xx,返回:hello yuntian,this is first messge,說明請求正常響應。

啓動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.

資料

相關文章
相關標籤/搜索