Skip to content
标签
网关
spring
字数
2533 字
阅读时间
10 分钟

一、概述

ZUUL是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

动态路由:动态将请求路由到不同后端集群

压力测试:逐渐增加指向集群的流量,以了解性能

负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求

静态响应处理:边缘位置进行响应,避免转发到内部集群

身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。

Spring Cloud对Zuul进行了整合和增强。

服务网关是在微服务前边设置一道屏障,请求先到服务网关,网关会对请求进行过滤、校验、路由、代理等处理。有了服务网关可以提高微服务的安全性,网关校验请求的合法性,请求不合法将被拦截,拒绝访问。

Zuul与Nginx在实际项目中配合使用,Nginx的作用是反向代理、负载均衡,Zuul的作用是保障微服务的安全访问,拦截微服务请求,校验合法性及负载均衡。

二、使用示例

xml
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
	<version>2.1.0.RELEASE</version>
</dependency>
yml
zuul:
	routes:
	manage‐course: #路由名称,名称任意,保持所有路由名称唯一
	  path:   #访问路径匹配
      serviceId: #指定服务id,从Eureka中找到服务的ip和端口
	  #url: http://localhost:8080 #也可指定url
	  strip‐prefix: false #true:代理转发时去掉前缀,false:代理转发时不去掉前缀
	  sensitiveHeaders: #默认zuul会屏蔽cookie,cookie不会传到下游服务,这里设置为空则取消默认的黑名单,如果设置了具体的头信息则不会传到下游服务
	# ignoredHeaders: Authorization 可以设置过虑的头信息,默认为空表示不过虑任何头
	
	zuul.routes.<route>.path=/xxx/** : 来指定映射路径。 <route> 是自定义的路由名
	zuul.routes.<route>.serviceId=/product-service :来指定服务名
	可以简化为一条 zuul: routes: shop-service-product: /product-service/**
	zuul会根据product-service服务名默认映射到/product-service/**

启动类添加@EnableZuulProxy // 开启Zuul的网关功能

三、过滤器

Zuul它包含了两个核心功能:对请求的路由和过滤。其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。

过滤器可以说是Zuul实现API网关功能最为核心的部件,每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。

Zuul 中的过滤器跟 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。

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

zuul中RequestContext对象

RequestContext:用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse都存储在RequestContext中。RequestContext扩展了ConcurrentHashMap,所以,任数据都可以存储在上下文中

java

// 自定义过虑器需要继承 ZuulFilter,ZuulFilter是一个抽象类,重写四个方法
public abstract ZuulFilter implements IZuulFilter{

    //返回字符串,代表过滤器的类型。包含以下4种
    //pre:请求在被路由之前执行
    //route/routing:在路由请求时调用
    //post:在route和errror过滤器之后调用
    //error:处理请求时发生错误调用
    abstract public String filterType();

    //过返回的int值来定义过滤器的执行顺序,数字越小优先级越高
    abstract public int filterOrder();
    
    //判断该过滤器是否需要执行。返回true执行,返回false不执行。
    boolean shouldFilter();// 来自IZuulFilter

    //过滤器的具体业务逻辑。
    Object run() throws ZuulException;// IZuulFilter
}

正常流程

请求到达首先会经过pre类型过滤器,而后到达route类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程

整个过程中,pre或者route过滤器出现异常,都会直接进入error过滤器,在error处理完毕后,会将请求交给POST过滤器,最后返回给用户。

如果是error过滤器自己出现异常,最终也会进入POST过滤器,将最终结果返回给请求客户端。

如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和route不同的是,请求不会再到达POST过滤器了。

使用场景

请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了

异常处理:一般会在error类型和post类型过滤器中结合来处理。

服务调用时长统计:pre和post结合使用。

四、流程分析

在Zuul中, 整个请求的过程是这样的,首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的zuulfilter共享。zuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的zuulfilter的管理器。FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。有了这些filter之后,zuulservelet首先执行的Pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。

初始化

根据自动装载原则可以在 spring- cloud-netflix-zuul-2.1.0.RELEASE.jar 下找到 spring.factories 加载了ZuulServerAutoConfiguration,ZuulProxyAutoConfiguration,是Zuul服务端的自动配置类。

ZuulProxyAutoConfiguration 继承了 ZuulServerAutoConfiguration,

ZuulServerAutoConfiguration配置类的作用:

CompositeRouteLocator:组合路由定位器,看入参就知道应该是会保存好多个RouteLocator,构造过程中其实仅包括一个DiscoveryClientRouteLocator。

SimpleRouteLocator:默认的路由定位器,主要负责维护配置文件中的路由配置。

ZuulController:Zuul创建的一个Controller,用于将请求交由ZuulServlet处理。

ZuulHandlerMapping:这个会添加到SpringMvc的HandlerMapping链中,只有选择了ZuulHandlerMapping的请求才能出发到Zuul的后续流程。

注册ZuulFilterInitializer,通过FilterLoader加载应用中所有的过滤器并将过滤器注册到FilterRegistry,

请求转发

ZuulHandlerMapping ,为SpringMVC中 HandlerMapping 的拓展实现,会自动的添加到HandlerMapping链中。

主要目的就是把所有路径的请求导入到ZuulController上.另外的功效是当觉察RouteLocator路由表变更,则更新自己dirty状态,重新注册所有Route到ZuulController

在 ZuulController 中的 handleRequest 方法,会调用已经注册的 ZuulServlet 完成业务请求。主要逻辑在ZuulServlet的service中执行。

过滤器

Zuul默认注入的过滤器可以在 spring-cloud-netflix-core.jar 中找到。

过滤器order描述类型
ServletDetectionFilter-3检测请求是用DispatcherServlet还是ZuulServletpre
Servlet30WrapperFilter-2在Servlet 3.0 下,包装requestspre
FormBodyWrapperFilter-1解析表单数据pre
SendErrorFilter0如果中途出现错误error
DebugFilter1设置请求过程是否开启debugpre
PreDecorationFilter5根据uri决定调用哪一个route过滤器pre
RibbonRoutingFilter10如果写配置的时候用ServiceId则用这个route过滤器,该过滤器可以用Ribbon做负载均衡,用hystrix做熔断route
SimpleHostRoutingFilter100如果写配置的时候用url则用这个route过滤route
SendForwardFilter500用RequestDispatcher请求转发route
SendResponseFilter1000用RequestDispatcher请求转发post

五、zuul存在问题

  • 性能问题

    Zuul1x版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。简单讲,每来一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成 容器无法接受新的请求。

  • 不支持任何长连接,如websocket