Spring Cloud Zuul 構建微服務網關

爲何要有服務網關?在使用微服務架構時,一個客戶端的業務需求可能會調用多個服務的接口,例如一次購物,須要調用商品服務,下單的時候要調用訂單服務(服務劃分更細的話還要調用更多的微服務)。客戶端直接跟各個服務通訊會有一些問題或者說弊端,客戶端的調用會複雜,更重要的是會有跨域請求問題和複雜的權限控制認證java

爲了對外服務的安全性,不得不在原有的服務接口上作有關權限控制的校驗邏輯,而這些權限相關的邏輯應該要把它從各個服務中抽離出來,做爲外部調用和各個服務之間的負載均衡器。服務網關就是這樣一個角色,對外統一 Rest API 接口,對內服務路由、負載均衡,同時還提供身份認證安全和監控功能git

Zuul

Zuul 是 Netflix 開源的服務網關,它的核心就是一系類的過濾器,經過一系列的過濾器在請求的各個階段進行處理,具體的路由器功能見 Spring Cloud Zuul 過濾器github

準備

這裏使用 Eureka Server 做爲服務註冊中心web

spring:
  application:
    name: eureka-server
server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

另外準備一個服務 product-servicespring

spring:
  application:
    name: product-service
server:
  port: 8071
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

構建 Zuul 微服務網關

建立 Spring Boot 工程命名爲 zuul-gateway 做爲服務網關,添加以下依賴json

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

在主類上添加註解 @EnableZuulProxy 開啓 Zuul 代理,這個代理會使用 Ribbon 獲取註冊服務的實例,同時還整合了 Hystrix 實現容錯,全部請求都會在 Hystrix 命令中執行。api

@SpringBootApplication
@EnableZuulProxy
public class GatewayZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayZuulApplication.class, args);
    }
}
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}

配置信息,添加 eureka 註冊中心的地址跨域

spring:
  application:
    name: gateway-zuul
server:
  port: 8090
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

到這裏一個簡單的服務網關就完成了,只是簡單吧把網關服務註冊到 Eureka,Spring Cloud Zuul 整合了 Eureka,會提供默認的服務路由功能,默認狀況下,Zuul 會代理全部註冊到 Eureka Server 上的微服務安全

gateway-zuul 的請求 /{serviceId}/** 會轉發到對應註冊在 Eureka Server 上對應 serviceId 的服務 /**
啓動 eureka-server、product-service、zuul-gateway,而後訪問 http://localhost:8090/product-service/product/1 ,該請求會被轉發到 product-service 的 /product/1架構

另外想要跳過一些服務能夠設置 zuul.ignoredServices,要得到對路由的更細粒度控制,能夠單獨指定路徑和 serviceId

zuul:
  ignoredServices: '*'
  routes:
    product:
      path: /product/**
      serviceId: product-service

上面設置的是除 product 服務以外的服務都忽略,而訪問 product-service 服務的路徑爲 /product/**

參考代碼見:demo

Zuul 路由端點

@EnableZuulProxy 註解配合 Spring Boot Actuator,Zuul 會暴露額外的兩個管理端點:RoutesFilters。分別是關於路由和過濾器的端點(過濾器的端點在這裏介紹 Spring Cloud Zuul 過濾器

spring-cloud-starter-netflix-zuul 已經依賴了 spring-boot-starter-actuator,因此上面的工程已經包含了路由管理的功能。關於路由端點的路徑爲 /routes/routes/details

訪問路徑 http://localhost:8090/actuator/routes

{
    "/product/**": "product-service"
}

訪問路徑 http://localhost:8090/actuator/routes/details,能夠查看路由的詳細信息

{
    "/product/**": {
        "id": "product",
        "fullPath": "/product/**",
        "location": "product-service",
  		"path": "/**",
        "prefix": "/product",
        "retryable": false,
        "customSensitiveHeaders": false,
        "prefixStripped": true
    }
}

訪問404是由於沒有暴露端點,能夠設置 management.endpoints.web.exposure.include: '*'

路由配置

默認狀況下,Zuul 網關會代理全部註冊到 Eureka Server 上的服務,但咱們能夠經過配置來讓其只代理其中一部分的服務或者是本身控制 URL。Zuul 的路由配置很是的靈活

自定義訪問路徑。zuul.routes.{serviceId}={costomUrl}

zuul:
  routes:
    product-service: /product/**

忽略指定服務,多個用逗號隔開,忽略所有用 *

zuul:
  ignored-services: product-service1,product-service2
zuul:
  ignored-services: '*'
  routes:
    product-service: /product/**
# 忽略全部服務,只路由 product-service

同時指定微服務的 serviceId 和對應路徑

zuul:
  ignoredServices: '*'
  routes:
    product:  # 只是一個路由名稱
      service-id: product-service
      path: /product/**

同時指定地址和訪問路徑

zuul:
  ignoredServices: '*'
  routes:
    product:
      url: http://localhost:8081/
      path: /product/**

添加路由前綴

zuul:
  perfix: /api
  strip-perfix: false
  routes:
    product-service: /product/**
# /api/product/**  -> /api/**
zuul:
  routes:
    product-service: /product/**
    strip-perfix: false
# /product/**  -> /product/**

忽略某些路徑

zuul:
  ignoredPatterns: /**/admin/** #忽略全部包含 /admin/ 的路徑
  routes:
    product-service: /product/**

Zuul 的容錯和回退

在 Spring Cloud 中,Zuul 已經默認整合了 Hystrix,關於 Hystrix 介紹和監控面板能夠見 Spring Cloud 斷路器 Hystrix。咱們使用上面的例子

由上圖能夠看出,Zuul 的 Hystrix 監控粒度爲服務,而不是接口。

接下來咱們能夠關閉服務 product-service,而後再訪問接口 http://localhost:8090/product/product/1

爲 Zuul 添加回退

爲 Zuul 添加回退,須要實現 FallbackProvider 接口,須要指定回退用於的路由ID,並提供一個 ClientHttpResponse 做爲回退返回

public class ProductFallbackProvider implements FallbackProvider {

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

    @Override
    public ClientHttpResponse fallbackResponse(String route, final 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("fallback".getBytes());
            }

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

若是您想爲全部路由提供默認的回退,您能夠建立一個類型爲 FallbackProvider 的bean,並讓 getRoute 方法返回 *null

重啓服務網關,訪問接口 http://localhost:8090/product/product/1

相關文章
相關標籤/搜索