Spring Cloud Zuul

  • 快速入门
  • 构建网关
  • 请求路由
  • 请求过滤
  • 路由详解
  • 服务路由配置
  • 服务路由的默认规则
  • 本地跳转
  • Cookie和头信息
  • Hystrix和Ribbon支持
  • 过滤器详解
  • 过滤器
  • 请求生命周期
  • 禁用过滤器


快速入门

构建网关

API网关服务的构建过程

  • 构建一个基础的Spring Boot工程,命名为api-gateway,修改pom.xml,引入spring-cloud-starter-zuul依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zuul</artifactId>
	<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-eureka</artifactId>
	<version>1.4.6.RELEASE</version>
</dependency>
  • 创建应用主类,使用@EnableZuulProxy注解开启Zuul的api网关服务功能
@EnableZuulProxy
@SpringBootApplication
public class HelloApplication {
	public static void main(String[] args) {
		SpringApplication.run(HelloApplication.class, args);
	}
}

请求路由

  • application.properties中配置Zuul应用的基础信息,指定Eureka注册中心的位置,并且配置服务路由
spring.application.name=api-gateway
server.port=5555
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=hello-service
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=feign-consumer
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

启动服务后,查看eureka-server的信息面板,可以看到API-GATEWAY已经在列表中

spring获取url中的参数_spring


经过配置,http://localhost:5555/api-a/hello符合/api-a/**的规则,最终/hello的请求会被发送到hello-service服务的某个实例上去

http://localhost:5555/api-b/feign-consumer符合/api-b/**的规则,最终/feign-consumer的请求会被发送到feign-consumer服务的某个实例上去

请求过滤

定义一个类来继承ZuulFilter抽象类并重写下面4个方法来实现自定义的过滤器

  • filterType:过滤器的类型,决定过滤器在请求的哪个生命周期中执行,定义为pre,代表会在请求被路由之前执行
    pre:可以在请求被路由之前调用
    routing:在路由请求时被调用
    post:在routing和error过滤器之后被调用
    error:处理请求时发生错误时被调用
  • filterOrder:过滤器的执行顺序.当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行
  • shouldFilter:判断该过滤器是否需要被执行,返回true表示该过滤器对所有请求都会生效
  • run:过滤器的具体逻辑,对于不满足条件的请求可以有如下设置
//zuul过滤该请求,不对其路由
ctx.setSendZuulResponse(false);
//设置返回的错误码
ctx.setResponseStatusCode(401);
//对返回的body内容进行编辑
ctx.setResponseBody("Your request has been lost!");

在实现了自定义过滤器之后,并不会直接生效,还需要在主类中创建具体的Bean

@EnableZuulProxy
@SpringBootApplication
public class HelloApplication {
	public static void main(String[] args) {
		SpringApplication.run(HelloApplication.class, args);
	}
	@Bean
	public AccessFilter accessFilter(){
		return new AccessFilter();
	}
}

总结Spring Cloud Zuul的优点:
1)作为系统的统一入口,屏蔽了系统内部各个微服务的细节
2)可以与服务治理框架结合,实现自动 化的服务实例维护以及负载均衡的路由转发
3)可以实现接口权限校验与微服务业务逻辑的解耦
4)通过服务网关中的过滤器,在各生命周期中去校验请求的内容,将原本在对外服务层做的校验前移,保证了微服务的无状态性,同时降低了微服务的测试难度,让服务本身更集中关注业务逻辑的处理

路由详解

服务路由配置

对于面向服务的路由配置,可以用zuul.routes.user-service=/user-service/**来配置
在Eureka的帮助下,API网关服务自身就已经维护了系统中所有serviceId与实例地址的映射关系

服务路由的默认规则

Spring Cloud Zuul构建的API网关服务引入Spring Cloud Eureka之后,它为Eureka中的每个服务都自动创建一个默认路由规则
对于不希望对外开放的服务可以通过zuul.ignored-services参数来设置一个服务名匹配表达式来定义不自动创建服务的规则,然后在配置文件中通过zuul.routes.<serviceId>=<path>配置方式来创建路由,其他的依然是使用默认规则

本地跳转

在Zuul实现的API网关路由功能中,还支持forward形式的服务端跳转配置

zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.url=http://localhost:8001/
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.url=forward:/local

当接收到/api-b/hello的请求,对于api-b的路由规则生效,请求会被转发到网关的/local/hello请求上进行本地处理,所以需要增加一个/local/hello的接口实现才可以

@RestController
public class HelloController{
	@RequestMapping("/local/hello")
	public String hello(){
		return "Hello World Local";
	}
	
}

Cookie和头信息

  • 通过指定路由的参数来配置,方法有两种
#方法一:对指定路由开启自定义敏感头
zuul.routes.<router>.customSensitiveHeaders=true
#方法二:将指定路由的敏感头设置为空
zuul.routes.<router>.sensitiveHeaders=

仅对指定的Web应用开启对敏感信息的传递,影响范围小,不至于引起其他服务的信息泄露问题
重定向问题描述:在登录完成后,通过重定向的方式跳到登录后的页面,指向了具体的服务实例地址
重定向问题解决方案:网关在进行路由转发之前为请求设置Host头信息,以标识最初的服务端请求地址,具体配置如下:

zuul.addHostHeader=true

Hystrix和Ribbon支持

Zuul拥有线程隔离和断路器自我保护功能,以及对服务调用的客户端负载均衡功能
当使用path和url的映射关系来配置路由的时候,不会采用HystrixCommand来包装,所以这类路由请求没有线程隔离和断路器的保护,并且也不会有负载均衡的能力.
所以,尽量使用path和serviceId的组合来进行配置,这样不仅保证API网关的健壮和稳定,也能用到Ribbon的客户端负载均衡功能
在使用Zuul搭建API网关的时候,可以通过Hystrix和Ribbon的参数来调整路由请求的各种超时时间等配置
在有些情况下,可能需要关闭重试机制,可以使用下面的配置:

#全局关闭重试机制
zuul.retryable=false
#指定路由关闭重试机制
zuul.routes.<route>.retryable=false

过滤器详解

过滤器

在Spring Cloud Zuul中实现的过滤器必须包含4个基本特征:过滤类型,执行顺序,执行条件,具体操作,具体含义前面已经讲过

String filterType();
int filterOrder();
boolean shouldFilter();
Object run();

请求生命周期

一个HTTP请求到达API网关之后,如何在各种不同类型的过滤器之间流转的详细过程

spring获取url中的参数_spring获取url中的参数_02


第一阶段pre:在进行请求路由之前做一些前置加工

第二阶段routing:将外部请求转发到具体服务实例上去的过程,当服务实例将请求结果都返回之后,routing阶段完成,请求进入第三阶段post

第三阶段post:此类型的过滤器在处理的时候不仅可以获取到请求信息,还能获取到服务实例的返回信息,所以在post类型的过滤器中,可以对处理结果进行一些加工或者转换等内容

特殊阶段error:此阶段只有上述三个阶段中发生异常时才会触发,但是最后的流向还是post过滤器,需要通过post过滤器将最终结果返回给请求客户端

禁用过滤器

对于过滤器不想使用了,想要禁用,在Zuul中特别提供了一个参数来禁用指定的过滤器,参数配置格式如下:

zuul.<SimpleClassName>.<filterType>.disable=true

其中<SimpleClassName>代表过滤器的类名,比如AccessFilter
<filterType>代表过滤器类型,比如AccessFilter的pre类型
比如想要禁用AccessFilter过滤器,只要在application.properties配置文件中增加如下配置:
zuul.AccessFilter.pre.disable=true 这样就可以禁用Spring Cloud Zuul中默认定义的核心过滤器,来实现一套更符合实际需求的处理机制