一、概述
zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。
Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
所有从设备或网站来的请求都会经过Zuul到达后端的Netflix应用程序。作为一个边界性质的应用程序,Zuul提供了动态路由、监控、弹性负载和安全功能。
二、原理
zuul的核心逻辑都是由一系列filter过滤器链实现的,但是filter的类型不同,执行的时机也不同,效果自然也不一样,主要特点如下:
- filter的类型:filter的类型,决定了它在整个filter链中的执行顺序,可能在端点路由前执行,也可能在端点路由时执行,还有可能在端点路由后执行,甚至是端点路由发生异常时执行。
- filter的执行顺序:同一种类型的filter,可以通过filterOrder()方法设置执行顺序,一般都是根据业务场景自定义filter执行顺序。
- filter执行条件:filter运行所需的标准,或条件。
- filter执行效果:符合某个filter执行条件,产生执行效果。
zuul内部有一套完整的机制,可以动态读取编译运行filter机制,filter与filter之间不直接通信,在请求线程中会通过RequestContext来共享状态,它内部是用ThreadLocal实现的,例如HttpServletRequest、HttpServletResponse、异常信息等。
zuul一共有4种不同的生命周期:
- pre:在zuul网关按照规则路由到下级服务之前执行,如果需要对请求进行预处理,可以使用这种类型的过滤器。如:认证鉴权,限流等。
- route:这种过滤器是zuul路由动作的执行者,是Apache HttpClient或Ribbon构建和发送原始HTTP请求的地方,现在也支持OKHTTP。
- post:这种过滤器是在端点请求完毕,返回结果或者发生异常后执行的filter。如果需要对返回的结果进行再次处理,可以在这种过滤中处理逻辑。
- error: 这种过滤器是在整个生命周期内,如果发生异常,就执行该filter,可以做全局异常处理。
三、实践
3.1路由演示
这个实践代码基于:SpringCloud教程四。
创建新的module,命名service-zuul,其pom.xml如下:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5
6 <groupId>com.wyma</groupId>
7 <artifactId>service-zuul</artifactId>
8 <version>0.0.1-SNAPSHOT</version>
9 <packaging>jar</packaging>
10
11 <name>service-zuul</name>
12 <description>Demo project for Spring Boot</description>
13
14 <parent>
15 <groupId>org.springframework.boot</groupId>
16 <artifactId>spring-boot-starter-parent</artifactId>
17 <version>1.5.2.RELEASE</version>
18 <relativePath/> <!-- lookup parent from repository -->
19 </parent>
20
21 <properties>
22 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
24 <java.version>1.8</java.version>
25 </properties>
26
27 <dependencies>
28 <dependency>
29 <groupId>org.springframework.cloud</groupId>
30 <artifactId>spring-cloud-starter-eureka</artifactId>
31 </dependency>
32 <dependency>
33 <groupId>org.springframework.cloud</groupId>
34 <artifactId>spring-cloud-starter-zuul</artifactId>
35 </dependency>
36 <dependency>
37 <groupId>org.springframework.boot</groupId>
38 <artifactId>spring-boot-starter-web</artifactId>
39 </dependency>
40
41 <dependency>
42 <groupId>org.springframework.boot</groupId>
43 <artifactId>spring-boot-starter-test</artifactId>
44 <scope>test</scope>
45 </dependency>
46 </dependencies>
47
48 <dependencyManagement>
49 <dependencies>
50 <dependency>
51 <groupId>org.springframework.cloud</groupId>
52 <artifactId>spring-cloud-dependencies</artifactId>
53 <version>Dalston.RC1</version>
54 <type>pom</type>
55 <scope>import</scope>
56 </dependency>
57 </dependencies>
58 </dependencyManagement>
59
60 <build>
61 <plugins>
62 <plugin>
63 <groupId>org.springframework.boot</groupId>
64 <artifactId>spring-boot-maven-plugin</artifactId>
65 </plugin>
66 </plugins>
67 </build>
68
69 <repositories>
70 <repository>
71 <id>spring-milestones</id>
72 <name>Spring Milestones</name>
73 <url>https://repo.spring.io/milestone</url>
74 <snapshots>
75 <enabled>false</enabled>
76 </snapshots>
77 </repository>
78 </repositories>
79
80
81 </project>
在入口applicaton类加上注解@EnableZuulProxy,开启zuul的功能:
1 @EnableZuulProxy
2 @EnableEurekaClient
3 @SpringBootApplication
4 public class ServiceZuulApplication {
5
6 public static void main(String[] args) {
7 SpringApplication.run(ServiceZuulApplication.class, args);
8 }
9
10
11
12 }
配置文件application.yml如下:
1 eureka:
2 client:
3 serviceUrl:
4 defaultZone: http://localhost:8761/eureka/
5 server:
6 port: 8769
7 spring:
8 application:
9 name: service-zuul
10 zuul:
11 routes:
12 api-a:
13 path: /api-a/**
14 serviceId: service-ribbon
15 api-b:
16 path: /api-b/**
17 serviceId: service-feign
首先指定服务注册中心的地址为http://localhost:8761/eureka/,服务的端口为8769,服务名为service-zuul;以/api-a/ 开头的请求都转发给service-ribbon服务;以/api-b/开头的请求都转发给service-feign服务;
依次运行这五个工程;打开浏览器访问:http://localhost:8769/api-a/hello?name=51ma;浏览器显示:
hello 51ma,i am from port:8763
打开浏览器访问:http://localhost:8769/api-b/hi?name=51ma;浏览器显示:
hello 51ma,i am from port:8763
说明zuul起到路由的作用。
3.2过滤演示
添加MyFilter过滤器,如下:
1 import com.netflix.zuul.ZuulFilter;
2 import com.netflix.zuul.context.RequestContext;
3 import org.slf4j.Logger;
4 import org.slf4j.LoggerFactory;
5 import org.springframework.stereotype.Component;
6
7 import javax.servlet.http.HttpServletRequest;
8
9 /**
10 * Created by 51ma on 2019/5/13
11 */
12 @Component
13 public class MyFilter extends ZuulFilter{
14
15 private static Logger log = LoggerFactory.getLogger(MyFilter.class);
16 @Override
17 public String filterType() {
18 return "pre";
19 }
20
21 @Override
22 public int filterOrder() {
23 return 0;
24 }
25
26 @Override
27 public boolean shouldFilter() {
28 return true;
29 }
30
31 @Override
32 public Object run() {
33 RequestContext ctx = RequestContext.getCurrentContext();
34 HttpServletRequest request = ctx.getRequest();
35 log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
36 Object accessToken = request.getParameter("token");
37 if(accessToken == null) {
38 log.warn("token is empty");
39 ctx.setSendZuulResponse(false);
40 ctx.setResponseStatusCode(401);
41 try {
42 ctx.getResponse().getWriter().write("token is empty");
43 }catch (Exception e){}
44
45 return null;
46 }
47 log.info("ok");
48 return null;
49 }
50 }
- filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
- pre:路由之前
- routing:路由之时
- post: 路由之后
- error:发送错误调用
- filterOrder:过滤的顺序
- shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
- run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
这时访问:http://localhost:8769/api-a/hello?name=51ma ;网页显示:
token is empty
访问 http://localhost:8769/api-a/hello?name=51ma&token=22 ; 网页显示:
hello 51ma,i am from port:8763