撸了今年阿里、头条和美团的面试,我有一个重要发现.......>>>

zuul是什么

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的工作原理

过滤器机制

zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。
zuul把Request route到 用户处理逻辑 的过程中,这些filter参与一些过滤处理,比如Authentication,Load Shedding等。

Zuul提供了一个框架,可以对过滤器进行动态的加载,编译,运行。

Zuul的过滤器之间没有直接的相互通信,他们之间通过一个RequestContext的静态类来进行数据传递的。RequestContext类中有ThreadLocal变量来记录每个Request所需要传递的数据。

Zuul的过滤器是由Groovy写成,这些过滤器文件被放在Zuul Server上的特定目录下面。Zuul会定期轮询这些目录,修改过的过滤器会动态的加载到Zuul Server中以便过滤请求使用。

下面有几种标准的过滤器类型:
Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。

  1. PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  2. ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  3. POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  4. ERROR:在其他阶段发生错误时执行该过滤器。

内置的特殊过滤器

zuul还提供了一类特殊的过滤器,分别为:StaticResponseFilter和SurgicalDebugFilter
StaticResponseFilter:StaticResponseFilter允许从Zuul本身生成响应,而不是将请求转发到源。
SurgicalDebugFilter:SurgicalDebugFilter允许将特定请求路由到分隔的调试集群或主机。

自定义的过滤器

除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。
例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

过滤器的生命周期

Zuul请求的生命周期如图,该图详细描述了各种类型的过滤器的执行顺序。

zuul 能做什么

Zuul可以通过加载动态过滤机制,从而实现以下各项功能:

  • 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
  • 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
  • 动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
  • 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
  • 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
  • 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
  • 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。

搭建zuul服务

首先搭建Eureka server和一个Eureka-Provider

在上一节从零开始搭建spring-cloud(1) ----eureka中拷贝spring-cloud-eureka-server到这个项目中。

在上一节从零开始搭建spring-cloud(1) ----eureka中拷贝spring-cloud-eureka-provider-A-1到这个项目中。

搭建zuul服务

新建项目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内容如下:

ErrorFilter.java

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 {@Overridepublic String filterType() {return ERROR_TYPE;}@Overridepublic int filterOrder() {return -1;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();System.out.println("这是ErrorFilter");return null;}
}

FirstPreFilter.java

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 {@Overridepublic String filterType() {return PRE_TYPE;}@Overridepublic int filterOrder() {return 0;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {System.out.println("这是第一个自定义Zuul Filter!");return null;}
}

SecondPreFilter.java

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 {@Overridepublic String filterType() {return PRE_TYPE;}@Overridepublic int filterOrder() {return 2;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {System.out.println("这是SecondPreFilter!");//从RequestContext获取上下文RequestContext ctx = RequestContext.getCurrentContext();//从上下文获取HttpServletRequestHttpServletRequest 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;}
}

ThirdPreFilter.java

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 {@Overridepublic String filterType() {return PRE_TYPE;}@Overridepublic int filterOrder() {return 3;}@Overridepublic boolean shouldFilter() {RequestContext ctx = RequestContext.getCurrentContext();//从上下文获取logic-is-success值,用于判断此Filter是否执行return (boolean)ctx.get("logic-is-success");}@Overridepublic Object run() throws ZuulException {System.out.println("这是ThirdPreFilter!");//从RequestContext获取上下文RequestContext ctx = RequestContext.getCurrentContext();//从上下文获取HttpServletRequestHttpServletRequest 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;}
}

PostFilter.java

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 {@Overridepublic String filterType() {return POST_TYPE;}@Overridepublic int filterOrder() {return 0;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {System.out.println("这是PostFilter!");//从RequestContext获取上下文RequestContext ctx = RequestContext.getCurrentContext();//处理返回中文乱码ctx.getResponse().setCharacterEncoding("GBK");//获取上下文中保存的responseBodyString 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-zuulzuul.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=10000000server.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转发有两种配置

根据 eureka server 的serviceId 转发

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

根据具体 URL 转发:

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=/**

从零开始搭建spring-cloud(5) ----zuul相关推荐

  1. 自己动手,使用Spring Initializr从零开始搭建Spring Cloud项目

    新建Project 这里使用的开发工具是IDEA,JDK版本1.8. 打开IDEA开发工具,File -> New -> Project 然后一步步往下设置,然后到这一步,选择Spring ...

  2. Spring Cloud(五) Zuul Filter

    前文回顾: Spring Cloud(一)Eureka Server-单体及集群搭建 Spring Cloud(二) 配置Eureka Client Spring Cloud(三) 熔断器Hystri ...

  3. 520、Java Spring Cloud Alibaba -【Spring Cloud Alibaba Zuul】 2021.11.02

    目录 1. Zuul 简介 2.Zuul 网关的引入 3.Zuul 网关的快速搭建 4.参考链接 1. Zuul 简介 Zuul 微服务网关是为 Spring Cloud Netflix 提供动态路由 ...

  4. Spring Cloud Netflix Zuul中的速率限制

    来源:SpringForAll社区 1.引言 Spring Cloud Netflix Zuul 是一个包含Netflix Zuul的开源网关.它为Spring Boot应用增加了一些特别的特性.不幸 ...

  5. maven 聚合工程 用spring boot 搭建 spring cloud 微服务 模块式开发项目

    项目的简单介绍: 项目采用maven聚合工程 用spring boot 搭建 spring cloud的微服务 模块式开发 项目的截图: 搭建开始: 能上图 我少打字 1.首先搭建maven的聚合工程 ...

  6. Spring Cloud实战Zuul统一异常处理

    Spring Cloud实战Zuul统一异常处理 Spring Cloud Zuul中自己实现的一些核心过滤器,以及这些过滤器在请求生命周期中的不同作用.我们会发现在这些核心过滤器中并没有实现erro ...

  7. 从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(三) (mini-cloud) 搭建认证服务(认证/资源分离版) oauth2.0 (中)

    本文承接上文<从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(三) (mini-cloud) 搭建认证服务(认证/资源分离版) oauth2.0 (上)> ...

  8. 从0到1手把手搭建spring cloud alibaba 微服务大型应用框架(十五) swagger篇 : gateway 集成swagger 与 knife4j实现在线api文档并嵌入到自己项目内

    背景 我们日常开发中基本都是协同开发的,当然极个别的项目整体前后端都是一个人开发的,当多人协作时,尤其是前后端人员协同开发时 必然会面临着前端需要了解后端api接口的情况,两个选择,提前设计好文档,然 ...

  9. 告诉老默我想学Spring Cloud了(新手篇):从0到1搭建Spring Cloud项目(实际项目开发的浓缩精华版)

    告诉老默我想学Spring Cloud了(新手篇):从0到1搭建Spring Cloud项目 一.前言 二.如何选择版本 2.1 SpringCloud 和 Spring Boot 版本选型 2.1. ...

  10. 微服务(三) 【手摸手带你搭建Spring Cloud】 Ribbon 什么是负载均衡?spring cloud如何实现负载均衡?ribbon负载均衡有几种策略?Ribbon是什么?

    在上一章,我介绍了springcloud的eureka搭建.我们做了服务注册.最后我们还介绍了一些续约,失效剔除等参数配置.已经不需要再通过手动输入ip去访问服务,而是通过中心只需要通过服务名就可以获 ...

最新文章

  1. Android:九宫格
  2. 电音之王 对64bit int 取模
  3. 记一次小米前端面试题(一面)2020.10.28
  4. android ListView 刷新卡顿问题
  5. Thrift 个人实战--Thrift 网络服务模型
  6. PyTorch 1.0 中文文档:自动求导机制
  7. 如何 tune spark jobs
  8. Map集合的遍历方式(3种)
  9. lenovo微型计算机如何拆t410,联想thinkpad T410S全面拆解
  10. java pdf 使用itextpdf插入页码
  11. java IO流基础 万字详解(从拷贝文件到模拟上传头像)
  12. webservice接口等待时间_调用webservice超时问题的解决
  13. 【机器学习原理实战01】Ridge回归模型
  14. sql创建查询视图语句
  15. 为什么培训机构出身的程序员,不敢告诉任何人?
  16. 学seo要知道什么代码
  17. GA 电商数据分析实践课
  18. linux aarch64启动不了,引导AArch64 Linux
  19. @StateObject和@ObservedObject有什么区别?
  20. 基于更深卷积网络的大规模图像识别

热门文章

  1. Python从数据库读取大量数据批量写入文件的方法
  2. Xshell无法启动:要继续使用此程序,您必须应用最新的更新或使用新版本
  3. 小米网抢购系统开发实践和我的个人观察
  4. Bug错误openssl_encrypt()
  5. PHP分页的limit与offset
  6. 打开闪光灯_用手机拍照这么久,你居然还不知道闪光灯怎么用
  7. 开发一个出生年份的下拉选择框供用户选择_你的下拉式菜单设计对了吗?
  8. 计算机性能在线测评,关于电脑性能测试的常见的几大方法
  9. 影驰名人堂送的机器人_玩转GTX 1080Ti名人堂显示屏 影驰全新魔盘使用教程
  10. java宠物小精灵,简单的Java口袋妖怪扑灭模拟器