zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。
zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 至关因而设备和 Netflix 流应用的 Web 网站后端全部请求的前门。
zuul的例子能够参考 netflix 在github上的 simple webapp,能够按照netflix 在github wiki 上文档说明来进行使用(https://github.com/Netflix/zuul/wiki)。前端
zuul的核心是一系列的filters, 其做用能够类比Servlet框架的Filter,或者AOP。
zuul把Request route到 用户处理逻辑 的过程当中,这些filter参与一些过滤处理,好比Authentication,Load Shedding等。java
Zuul提供了一个框架,能够对过滤器进行动态的加载,编译,运行。git
Zuul的过滤器之间没有直接的相互通讯,他们之间经过一个RequestContext的静态类来进行数据传递的。RequestContext类中有ThreadLocal变量来记录每一个Request所须要传递的数据。github
Zuul的过滤器是由Groovy写成,这些过滤器文件被放在Zuul Server上的特定目录下面。Zuul会按期轮询这些目录,修改过的过滤器会动态的加载到Zuul Server中以便过滤请求使用。web
下面有几种标准的过滤器类型:
Zuul大部分功能都是经过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。spring
内置的特殊过滤器apache
zuul还提供了一类特殊的过滤器,分别为:StaticResponseFilter和SurgicalDebugFilter
StaticResponseFilter:StaticResponseFilter容许从Zuul自己生成响应,而不是将请求转发到源。
SurgicalDebugFilter:SurgicalDebugFilter容许将特定请求路由到分隔的调试集群或主机。后端
自定义的过滤器安全
除了默认的过滤器类型,Zuul还容许咱们建立自定义的过滤器类型。
例如,咱们能够定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。服务器
Zuul请求的生命周期如图,该图详细描述了各类类型的过滤器的执行顺序。
Zuul能够经过加载动态过滤机制,从而实现如下各项功能:
在上一节从零开始搭建spring-cloud(1) ----eureka中拷贝spring-cloud-eureka-server到这个项目中。
在上一节从零开始搭建spring-cloud(1) ----eureka中拷贝spring-cloud-eureka-provider-A-1到这个项目中。
新建项目spring-cloud-zuul,pom.xml内容以下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.vincent</groupId> <artifactId>spring-cloud-zuul</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.4.RELEASE</version> <scope>import</scope> <type>pom</type> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> </project>
咱们都知道,在zuul过滤器里PRE_TYPE类型是在路由前执行的,因此我要给你们演示配置三个PRE_TYPE类型的过滤器,按照顺序依次处理不一样的业务。以及,三个PRE_TYPE类型过滤器中任意一个出现异常时他的下游业务应该怎么处理。
咱们首先看一下项目的目录结构:
各个Filter内容以下:
package com.vincent.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ERROR_TYPE; /** * @author vincent * @time 2019-06-23 16:15 */ @Component public class ErrorFilter extends ZuulFilter { @Override public String filterType() { return ERROR_TYPE; } @Override public int filterOrder() { return -1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); System.out.println("这是ErrorFilter"); return null; } }
package com.vincent.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; /** * @author vincent * @time 2019-06-23 16:17 */ @Component public class FirstPreFilter extends ZuulFilter { @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { System.out.println("这是第一个自定义Zuul Filter!"); return null; } }
package com.vincent.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; /** * @author vincent * @time 2019-06-23 16:18 */ @Component public class SecondPreFilter extends ZuulFilter { @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return 2; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { System.out.println("这是SecondPreFilter!"); //从RequestContext获取上下文 RequestContext ctx = RequestContext.getCurrentContext(); //从上下文获取HttpServletRequest HttpServletRequest request = ctx.getRequest(); //从request尝试获取a参数值 String a = request.getParameter("a"); //若是a参数值为空则进入此逻辑 if (null == a) { //对该请求禁止路由,也就是禁止访问下游服务 ctx.setSendZuulResponse(false); //设定responseBody供PostFilter使用 ctx.setResponseBody("{\"status\":500,\"message\":\"a param is null\"}"); //logic-is-success保存于上下文,做为同类型下游Filter的执行开关 ctx.set("logic-is-success", false); //到这里此Filter逻辑结束 return null; } //设置避免报空 ctx.set("logic-is-success", true); return null; } }
package com.vincent.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; /** * @author vincent * @time 2019-06-23 16:19 */ @Component public class ThirdPreFilter extends ZuulFilter { @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return 3; } @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); //从上下文获取logic-is-success值,用于判断此Filter是否执行 return (boolean)ctx.get("logic-is-success"); } @Override public Object run() throws ZuulException { System.out.println("这是ThirdPreFilter!"); //从RequestContext获取上下文 RequestContext ctx = RequestContext.getCurrentContext(); //从上下文获取HttpServletRequest HttpServletRequest request = ctx.getRequest(); //从request尝试获取b参数值 String b = request.getParameter("b"); //若是b参数值为空则进入此逻辑 if (null == b) { //对该请求禁止路由,也就是禁止访问下游服务 ctx.setSendZuulResponse(false); //设定responseBody供PostFilter使用 ctx.setResponseBody("{\"status\":500,\"message\":\"b param is null\"}"); //logic-is-success保存于上下文,做为同类型下游Filter的执行开关,假定后续还有自定义Filter当设置此值 ctx.set("logic-is-success", false); //到这里此Filter逻辑结束 return null; } return null; } }
package com.vincent.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.POST_TYPE; /** * @author vincent * @time 2019-06-23 16:20 */ public class PostFilter extends ZuulFilter { @Override public String filterType() { return POST_TYPE; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { System.out.println("这是PostFilter!"); //从RequestContext获取上下文 RequestContext ctx = RequestContext.getCurrentContext(); //处理返回中文乱码 ctx.getResponse().setCharacterEncoding("GBK"); //获取上下文中保存的responseBody String responseBody = ctx.getResponseBody(); //若是responseBody不为空,则说明流程有异常发生 if (null != responseBody) { //设定返回状态码 ctx.setResponseStatusCode(500); //替换响应报文 ctx.setResponseBody(responseBody); } return null; } }
新建App.java,内容以下:
@SpringBootApplication @EnableZuulProxy public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
咱们开始配置application.properties,内容以下:
eureka.client.service-url.defaultZone=http://127.0.0.1:8080/eureka/ spring.application.name=service-zuul zuul.routes.users.url=http://localhost:8081/ zuul.routes.users.path=/** zuul.ignored-headers=Access-Controller-Allow-Credentials, Access-Control-Allow-Origin zuul.host.connect-timeout-millis=10000000 zuul.host.socket-timeout-millis=10000000 server.port=8082
这里zuul的匹配规则是经过url进行匹配。
zuul.host.connect-timeout-millis=10000000
zuul.host.socket-timeout-millis=10000000这两句的做用是防止服务提供方返回响应的时间过长
先添加参数访问微服务
控制台输出结果以下:
这是第一个自定义Zuul Filter! 这是SecondPreFilter! 这是PostFilter!
添加参数a访问,
控制台输出结果以下:
这是第一个自定义Zuul Filter! 这是SecondPreFilter! 这是ThirdPreFilter! 这是PostFilter!
添加参数a和参数b访问
控制台输出结果以下:
2019-06-23 17:29:44.348 INFO 3363 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration 这是第一个自定义Zuul Filter! 这是SecondPreFilter! 这是ThirdPreFilter! 这是PostFilter!
zuul.routes.serviceName.path=/exampleService/** zuul.routes.serviceName.serviceId=serviceId
注:
zuul.routes 是固定的
serviceName 是能够随便写的,但最好根据要路由的服务取
serviceId 是 eureka 服务注册时的名称
exampleService 是前端请求某个微服务的一个公共的路径名,如/users
而微服务在 Controller层的 RequestMapping 注解中能够不包含/users
例如本项目中的配置以下:
zuul.routes.myservice.path=/** zuul.routes.myservice.service-id=service-provider-A
zuul.routes.serviceName.path=/exampleService/** zuul.routes.serviceName.url=http://127.0.0.1:8080/
若是项目还没有使用eureka,能够采用了第二种转发规则。这种转发有不少好处,最大的好处就是能够很好地过渡到Spring Cloud,使用Zuul能够直接HTTP调用,方便不少。
例如本项目中的URL规则转发以下:
zuul.routes.users.url=http://localhost:8081/ zuul.routes.users.path=/**