SpringCloud(四):服务网关Zuul

/ SpringCloud / 1 条评论 / 960人围观

在微服务的架构中,服务网关就是一个介于客户端与服务端之间的中间层。在这种情况下,客户端只需要跟服务网关交互,无需调用具体的微服务接口。这样的好处在于,客户端可以降低复杂性;对于需要认证的服务,只需要在服务网关配置即可;同样也方便后期微服务的变更和重构,即微服务接口变更只需在服务网关调整配置即可,无需更改客户端代码。

Zuul入门

引入jar包

跟Eureka一样,SpringBoot2.+将此jar包移到了netflix下,以前在spring-cloud-starter包下。

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

请输入图片描述

因为spring-cloud-starter-netflix-zuul依赖已经包含了Hystrix和Ribbon,所以zuul支持前面介绍的Hystrix和Ribbon相关配置。

启动类注解

@SpringBootApplication
@EnableZuulProxy// 使用zuul代理
public class StoryZuulApplication {

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

}

配置文件

spring:
  application:
    name: Service-Zuul
server:
  port: 8861

路由配置

传统路由规则

在配置文件中加入:

zuul:
  routes:
    api-a:
      path: /api-a/**
      url: https://localhost:8081

通过上面的配置,所有符合/api-a/xxx规则的访问都将被路由转发到https://localhost:8081/地址上,即当我们向服务网关访问https://localhost:8861/api-a/hello请求的时候,请求将被转发到https://localhost:8081/hello服务上。

服务名称路由

传统的配置方式不便之处在于需要知道服务的具体地址和端口号等信息,咱可以借助Eureka来实现通过服务名称配置路由。在Zuul-Gateway项目中引入Eureka依赖。

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

然后在入口类中加入@EnableDiscoveryClient注解,使其具有获取服务的能力。并添加配置文件,如下:

spring:
  application:
    name: Service-Zuul
server:
  port: 8861

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    serviceUrl:
      defaultZone: https://localhost:8080/eureka/

zuul:
  routes:
    api-b:
      path: /api-b/**
      serviceId: server-provider
    api-c:
      path: /api-c/**
      serviceId: server-consumer

以上,首先指明了Eureka服务注册中心的地址,然后配置了api-b和api-c的路由,它们都是通过serviceId来指定服务名称的。启动Zuul-Gateway,访问:https://localhost:8861/api-b/hello即可由网关自动路由到相应地址。

基于服务名称的路由配置还可以进行简化,如下:

zuul:
  routes:
    server-provider:
      path: /api-b/**
    server-consumer:
      path: /api-c/**

默认路由规则

尝试访问https://localhost:8861/server-consumer/getInfo,可以得到返回结果,但Zuul工程的配置文件中并没有相应的路由规则,却访问成功了,原因就是Zuul有自己的默认路由规则,当使用服务名称作为请求的前缀路径时,实际上就会匹配上类似下面的默认路由配置:

zuul:
  routes:
    server-consumer:
      path: /server-consumer/**
      serviceId: server-consumer

如果不想使用默认的路由规则,仅需在配置文件中加入如下:

zuul:
  ignored-services: server-consumer

设置为zuul.ignored-services=※的时候将关闭所有默认路由配置规则。

路由优先级

假如某个请求路径可以和多个路由配置规则相匹配的话,Zuul根据匹配的先后顺序来决定最终使用哪个路由配置。比如:

zuul:
  routes:
    api-c:
      path: /api-c/**
      serviceId: server-consumer
    api-d:
      path: /api-c/user/1
      serviceId: nmys

当我们访问https://localhost:8861/api-c/user/1的时候,api-c和api-d的路由配置都可以匹配上,但由于api-c先于api-d配置,所以最终生效的是api-c的配置。假如将api-c和api-d的配置顺序调换,再次访问https://localhost:8861/api-c/user/1时将抛出异常,原因是不存在服务名为nmys的服务,并抛出异常:

Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: nmys

路由前缀配置

在配置文件加上如下:

zuul:
  prefix: /nmys

这样配置后,通过Zuul网关获取服务的时候,路径也得加上这个前缀,如https://localhost:8861/nmys/api-c/user/1

路由本地跳转

Zuul网关除了支持将服务转发到各个微服务上之外,还支持将服务跳转到网关本身的服务上,比如现在yml中有如下一段配置:

zuul:
  routes:
    api-e:
      path: /api-e/**
      url: forward:/test

当访问https://localhost:8861/api-e/hello时,Zuul会从本地/test/hello获取服务。

Zuul高级

头部过滤 & 重定向

在使用Zuul网关的时候你可能会遇到Cookie丢失的情况,这是因为默认情况下Zuul会过滤掉HTTP请求头中的一些敏感信息,这些敏感信息通过下面的配置设定:

zuul:
  sensitive-headers: Cookie,Set-Cookie,Authorization

如果想关闭这个默认配置,通过设置全局参数为空来覆盖默认值:

zuul:
  sensitive-headers:

如果只想关闭某个路由的HTTP请求头过滤,可以这样:

zuul:
 routes:
   api-a:
     sensitive-headers:

使用Zuul另一个常见问题是重定向的问题,可以通过下面的设置解决:

zuul:
  add-host-header: true

核心过滤器

Zuul另一个核心的功能就是请求过滤。Zuul中默认定义了4种不同生命周期的过滤器类型,在如下图所示:

请输入图片描述

这4种过滤器处于不同的生命周期,所以其职责也各不相同:

以下是默认实现的过滤器:

请输入图片描述

这些过滤器的优先级和作用如下表所示:

生命周期优先级过滤器功能描述
pre-3ServletDetectionFilter标记处理Servlet的类型
pre-2Servlet30WrapperFilter包装HttpServletRequest请求
pre-1FormBodyWrapperFilter包装请求体
route1DebugFilter标记调试标志
route5PreDecorationFilter处理请求上下文供后续使用
route10RibbonRoutingFilterserviceId请求转发
route100SimpleHostRoutingFilterurl请求转发
route500SendForwardFilterforward请求转发
post0SendErrorFilter处理有错误的请求响应
post1000SendResponseFilter处理正常的请求响应

其中优先级数字越小,优先级越高。要关闭这些过滤器可以在applicaiton.yml中按照格式配置即可。比如关闭SendResponseFilter过滤器:

zuul:
  SendResponseFilter:
    post:
      disable:
        true

自定义过滤器

自定义Zuul过滤器只需要继承ZuulFilter,然后实现以下四个抽象方法即可:

/**
 * description
 *
 * @author 70KG
 * @date 2018/10/29
 */
public class PreSendForwardFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return null;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return false;
    }

    @Override
    public Object run() {
        return null;
    }

}

完善上面代码:

/**
 * description
 *
 * @author 70KG
 * @date 2018/10/29
 */
@Component
@Slf4j
public class PreSendForwardFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        String host = request.getRemoteHost();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        log.info("请求URI:{},HTTP Method:{},请求IP:{}", uri, method, host);
        return null;
    }
}

这时候访问https://localhost:8861/api-b/getInfomations,控制台将打印出:

请求URI:/api-b/getInfomations,HTTP Method:GET,请求IP:0:0:0:0:0:0:0:1

  1. 你个傻屌

    回复