Spring Cloud之服务网关Zuul

在关于Spring Cloud系列之前的文章中,我们对负载均衡、注册中心、配置中心等微服务都做了介绍,已经满足大部分业务场景下的需求。但是仍存在一些问题,比如外部应用应该如何调用后端的各种API服务。此时可以引入网关服务,即Spring Cloud Zuul。它可以帮助我们实现诸如权限控制、服务调用、负载均衡等功能。如下图所示,Zuul主要充当了Api Gateway中的角色。

image

1. 准备工作

在使用网关服务,首先要构建上图中的Service和ServiceB,然后再启动一个Eureka的注册中心。为了方便起见,我是直接使用上一篇博文:配置中心 中的工程。依赖服务列表为:

服务 name 端口
注册中心 eureka 1111
Service A CONFIG-CLIENT 9092

2. 构建网关服务

2.1 新建Zuul工程

新建一个普通的Spring Cloud工程,加入如下依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

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

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

2.2 配置文件修改

修改application.properties文件,加入相关配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#server
server.port=5050
spring.application.name=API-GATEWAY

#eureka
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

#zuul-url
zuul.routes.baidu.path=/baidu/**
zuul.routes.baidu.url=https://www.baidu.com/

#zuul-serviceId
zuul.routes.configTest.path=/config/**
zuul.routes.configTest.serviceId=CONFIG-CLIENT

注意Zuul在api转发时有两种模式,第一种是写死了url的模式,匹配请求后,直接向url请求内容,本次我以baidu为例。第二种是服务化的模式,匹配了请求后,向某个在注册中心注册过的服务发出请求,本次以配置中心的客户端为例。

2.3 启动并测试

在主类中加入注解:

1
@EnableZuulProxy

开启Zuul服务的功能。

2.3.1 测试url模式

打开 http://localhost:5050/baidu ,匹配到了zuul.routes.baidu.path,其对应的url是zuul.routes.baidu.url=https://www.baidu.com/ ,则本次访问返回的内容如下:
image

2.3.2 测试serviceID模式

打开 http://localhost:5050/config ,匹配到zuul.routes.configTest.path,返回的内容如下:
image

3 服务过滤

过滤器负责对请求进行干预处理,同时对请求进行校验。过滤器可以说是Zuul实现API网关服务的核心组件,每一个进入到Zuul中的请求都应该经过过滤器的处理并返回给客户端。

3.1 接口介绍

Zuul的过滤器基本接口ZuulFilter,包括以下4个抽象方法:

1
2
3
4
public abstract String filterType();
public abstract int filterOrder();
boolean shouldFilter();
Object run();

filterType:代表过滤器的类型。Zuul中默认定义了4种不同的过滤器,分别如下:

  • pre:被路由之前调用。通常用于身份校验,访问记录等。
  • routing:在路由请求时被调用。构建分发给微服务的请求,可以使用ribbon请求服务。
  • post:routing和error过滤器之后调用。将内容返回给客户端。
  • error:请求发生错误时调用。

filterOrder:过滤的执行顺序,int越小则优先级越高。

shouldFilter:返回boolean,判断该过滤器是否执行。

run:过滤器的实现。

3.2 过滤器的生命周期

Zuul中定义了4个不同生命周期的过滤器类型,这基本覆盖了从接到API请求到返回内容的所有过程,下图是Zuul官方给出的请求生命周期的图解。

image

如上图所示,当外部API请求到达服务网关时,首先进入pre的过滤器,pre过滤器一般会对请求进行校验,比如accessToken等。完成pre类型的过滤器之后,进入第二阶段的routing过滤器,此时会把请求转发到具体的实例上。当实例的内容返回后,进入到post过滤器,会对处理结果进行一定的加工或者转换。此外,如果过程出错,则会进入到error阶段,error处理完之后也会进入到post阶段,由post过滤器将最终结果返回给客户端。

3.3 pre过滤器实现

本例子结合第二章的内容,实现一个pre类型的过滤器。
首先新建ApiFilter.java类,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private static final Logger LOG = LoggerFactory.getLogger(ApiFilter.class);

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

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

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

@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("accessToken");
if (null == token || "".equals(token)) {
LOG.warn("Token is empty...");
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(403);
return null;
}
LOG.info("Access: " + request.getRemoteAddr() + " Token: " + token);
return null;
}

run函数的内容,主要是获取accessToken并进行简单的校验。创建完过滤器之后,在主类中将其实例化:

1
2
3
4
@Bean
public ApiFilter apiFilter() {
return new ApiFilter();
}

此时,再输入 http://localhost:5050/config 后,就会出现权限不足的提示:

image

加上简单的accessToken后,http://localhost:5050/config?accessToken=zhaoyh ,可以正常访问:

image

通过以上例子可以看出,我们可以使用pre类型的过滤器做一些校验的工作,实际的项目中可以结合Shiro、oauth2.0等技术做鉴权的实现。

4 服务网关高可用

本篇内容,对于Spring Cloud Zuul网关服务做了基本的介绍和实现,但是在实际的项目中,还是远远不够的。比如当后端的服务出现异常时,可以结合Hystrix熔断机制,对返回内容进行拦截或者返回定制的内容。另外,当出现网络问题,或者负载过高导致后端服务出现延迟时,也需要对服务进行重试,此时可以结合Spring Retry技术,开启重试的功能。

以上内容就是关于Spring Cloud微服务框架之服务网关Zuul的全部内容了,谢谢你阅读到了这里!

Author:zhaoyh