【Spring Cloud 系列 六】Zuul 构建微服务网关

2018-09-03  本文已影响34人  司鑫

一 为什么需要使用微服务网关


当使用微服务架构时,我们会将单体应用拆分成很多小的服务,每个服务一般都会有不同的网络地址,而客户端可能需要调用多个服务接口才能完成一个业务需求,这样客户端就会直接与各个服务进行通信,造成以下可能出现的问题:

以上问题都可以通过加入网关来解决,网关是介于客户端和微服务端之间的中间层,所有的外部请求都需要先经过微服务网关,然后由网关在进行进一步的处理。封装了应用程序的内部结构,客户端只需要与网关打交道。


二 Zuul 简介


Zuul 是 Netflix 开源的微服务网关,可以和 Eureka、Ribbon、Hystrix 等组件配合使用。Zuul 的核心是提供了一些列的过滤器,完成下列功能:

Spring Cloud 对 Zuul 进行了整合与增强,Zuul 目前使用的默认HTTP 客户端是 Apache HTTP client,也可以设置使用 RestClient 或者 okhttp3.OKHttpClient,只需要设置对应的配置ribbon.restclient.enabled=trueribbon.okhttp.enabled=true

三 编写 Zuul 微服务网关


1 创建一个新项目(microservice-gateway-zuul),并添加相关依赖
 compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-zuul', version:'1.4.0.RELEASE'
    compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-eureka-client', version:'1.4.0.RELEASE'
2 在启动类上添加注解@EnableZuulProxy,声明一个代理,该代理同时还整合了 Ribbon 和 Hystrix
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
  public static void main(String[] args) {
    SpringApplication.run(ZuulApplication.class, args);
  }
}

3 编写配置文件application.yml
server:
  port: 8040
spring:
  application:
    name: microservice-gateway-zuul
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
4 启动相关的服务

当访问 http://localhost:8040/microservice-consumer-movie/user/1 时,请求会被转发到 mocroservice-consumer-movie-ribbon服务中的 /user/1 接口。

四 路由配置详解


在上个demo中我们对不同的service的 application name 进行了代理,我们还可以使用 Zull 代理部分服务,或者对 URL 进行更加准确的控制

自定义指定微服务的访问路径

配置 zuul.routes.「指定微服务的 serviceId」= 指定路径,例如

zuul:
  routes:
    microservice-provider-user: /user/**
通配符 含义 举例 解释
? 匹配任意单个字符 /feign-consumer/? 匹配/feign-consumer/a,/feign-consumer/b,/feign-consumer/c等
* 匹配任意数量的字符 /feign-consumer/* 匹配/feign-consumer/aaa,feign-consumer/bbb,/feign-consumer/ccc等,无法匹配/feign-consumer/a/b/c
** 匹配任意数量的字符 /feign-consumer/* 匹配/feign-consumer/aaa,feign-consumer/bbb,/feign-consumer/ccc等,也可以匹配/feign-consumer/a/b/c
忽略指定微服务
zuul:
  ignored-services: microservice-provider-user,microservice-consumer-movie // 多个服务使用 , 分割
忽略所有服务,只路由指定的服务
zuul:
  ignored-services: '*'   # 使用'*'可忽略所有微服务
  routes:
    microservice-provider-user: /user/**
同时指定服务的 serviceId 和 对应路径
zuul:
  routes:
    user-route:                   # 该配置方式中,user-route只是给路由一个名称,可以任意起名。
      service-id: microservice-provider-user
      path: /user/**              # service-id对应的路径
同时指定 path 和 URL
zuul:
  routes:
    user-route:                   # 该配置方式中,user-route只是给路由一个名称,可以任意起名。
      url: http://localhost:8000/ # 指定的url
      path: /user/**              # url对应的路径。

这种配置方式的路由 HystrixRibbon 都不会工作

同时指定 URL 和 path,并且 HystrixRibbon 可以正常工作
zuul:
  routes:
    user-route:
      path: /user/**
      service-id: microservice-provider-user
ribbon:
  eureka:
    enabled: false    # 禁用掉ribbon的eureka使用。详见:http://cloud.spring.io/spring-cloud-static/Camden.SR3/#_example_disable_eureka_use_in_ribbon
microservice-provider-user:
  ribbon:
    listOfServers: localhost:8000,localhost:8001
路由前缀
zuul:
  prefix: /api
  strip-prefix: false
  routes:
    microservice-provider-user: /user/**

当访问 Zuul 的 /api/mocroservice-provide-user/1 时会被转发到 microservice-provider-user/api/1

忽略某些路径
zuul:
  ignoredPatterns: /**/admin/**   # 忽略所有包括/admin/的路径
  routes:
    microservice-provider-user: /user/**
五 Zuul 的安全与 Header
敏感 Header 的设置

不同服务之间通信时会共享 Header,可以对敏感的 Header 进行设置,避免外泄

zuul:
  routes:
    user-route:
      path: /user/**
      service-id: microservice-provider-user
      sensitive-headers: Cookie, Set-Cookie ... 
忽略 Header
zuul:
  ignored-headers: Header 1, header 2

默认情况下,zuul.ignored-headers 是空值,但如果项目中存在 Spring security的依赖,那么 zuul.ignored-headers 就会存在一些header,如果我们需要使用下游服务中的 header 时,需要将 zuul.ingoredSecurity-Headers设置为 false

六 Zuul 的容错与回退


在 Spring Cloud 中,Zuul 已经默认整合了 hystrix。想要为 Zuul 添加回退,需要实现 ZuulFallbackProvider 接口,在实现类中,指定为哪个服务提供回退,并提供一个 ClientResponse 作为回退响应,例如:

@Component
public class MyFallbackProvider implements FallbackProvider {
  @Override
  public String getRoute() {
    // 表明是为哪个微服务提供回退,*表示为所有微服务提供回退
    return "*";
  }

  @Override
  public ClientHttpResponse fallbackResponse(Throwable cause) {
    if (cause instanceof HystrixTimeoutException) {
      return response(HttpStatus.GATEWAY_TIMEOUT);
    } else {
      return this.fallbackResponse();
    }
  }

  @Override
  public ClientHttpResponse fallbackResponse() {
    return this.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() {
        // headers设定
        HttpHeaders headers = new HttpHeaders();
        MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
        headers.setContentType(mt);
        return headers;
      }
    };
  }
}
上一篇下一篇

猜你喜欢

热点阅读