Spring Cloud Zuul 路由转发、过滤器(一)

1、简介

Zuul是Spring Cloud中的微服务网关。

网关:是一个网络整体系统中的前置门户入口。请求首先通过网关,进行路径的路由,定位到具体的服务节点上。

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

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

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

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

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

5)身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行

了整合和增强。

网关有以下几个作用:

1)统一入口:未全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。

2)鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。

3)动态路由:动态的将请求路由到不同的后端集群中。

4)减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。

2、Nginx配置

location /api-order {

proxy_pass http://127.0.0.1:9001/;

}

location /api-product {

proxy_pass http://127.0.0.1:9002/;

3、服务器端搭建

1)创建项目引入依赖

在IDEA中创建ZUUL网关工程shop_zuul_server,并添加响应依赖

2编写启动类

创建启动类 ZuulServerApplication

注意@EnableZuulProxy : 通过 @EnableZuulProxy 注解开启Zuul网管功能

3)新增修改yaml配置

创建配置文件application.yml,并添加相应配置

4、路由转发

最直观的理解:“路由”是指根据请求URL,将请求分配到对应的处理程序。在微服务体系中,Zuul负责接收所有的请求。根据不同的URL匹配规则,将不同的请求转发到不同的微服务处理。

只需要在application.yml文件中配置路由规则即可:

1)product-service:配置路由id,可以随意取名

2)url:映射路径对应的实际url地址

3)path:配置映射路径,这里将所有请求前缀为/product-service/的请求,转发到http://127.0.0.1:9002处理

配置好Zuul路由之后启动服务,在浏览器中输入http://localhost:8080/productservice/product/1,即可访问到订单微服务。

4.1 面向服务的路由

微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。如果对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合理。

Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的路由配置。

1添加Eureka客户端依赖

2)开启Eureka客户端发现功能

(2)加Eureka配置,获取服务信息

4)修改映射配置,通过服务名称获取

因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。

最后依次启动Eureka,商品微服务,API网关,在浏览器上通过访问http://localhost:8080/productservice/product/1 查看最终效果。

4.2 简化配置

在刚才的配置中,我们的规则是这样的:

zuul.routes.<route>.path=/xxx/** : 来指定映射路径。 <route> 是自定义的路由名

zuul.routes.<route>.serviceId=/product-service :来指定服务名。

而大多数情况下,我们的 <route> 路由名称往往和服务名会写成一样的。因此Zuul就提供了一种简化的配置语法: zuul.routes.<serviceId>=<path>

上面的配置可以简化为一条:

4.3 默认路由规则

在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。

因此Zuul就指定了默认的路由规则:

默认情况下,一切服务的映射路径就是服务名本身。例如服务名为: shop-service-product ,则默认的映射路径就是: /shop-serviceproduct/**

5、过滤器

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

1)PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

2)ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

3)POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端等。

4)ERROR:在其他阶段发生错误时执行该过滤器。Zuul提供了自定义过滤器的功能实现起来也十分简单,只需要编写一个类去实现zuul提供的接口

ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。run :过滤器的具体业务逻辑。filterType :返回字符串,代表过滤器的类型。包含以下4种:1)pre :请求在被路由之前执行

2)routing :在路由请求时调用

3)post :在routing和errror过滤器之后调用

4)error :处理请求时发生错误调用

5)filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

内置过滤器列表如下:

5.自定义过滤器

自定义一个过滤器,模拟一个登录的校验。基本逻辑:如果请求中有access-token参数,

则认为请求有效,放行。

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

6、核心源代码解读

在Zuul中, 整个请求的过程是这样的,首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的zuulfilter共享。

zuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的zuulfilter的管理器。FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。有了这些filter之后,zuulservelet首先执行的Pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。

6.1初始化

6.2请求转发

在Zuul的自动配置中我们看到了ZuulHandlerMapping,为SpringMVC中HandlerMapping的拓展实现,会自动的添加到HandlerMapping链中。

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

在 ZuulController 中的 handleRequest 方法,会调用已经注册的 ZuulServlet 完成业务请求,我们进入 ZuulServlet 看下内部是如何处理的

6.3过滤器

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

6、Zuul替代方案

在实际使用中我们会发现直接使用Zuul会存在诸多问题,包括:

1)性能问题

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

2)不支持任何长连接,如websocket替代方案

1Zuul2.x版本

Zuul 1.x 是一个基于阻塞 IO 的 API Gateway 以及 Servlet;直到 2018 年 5 月,Zuul 2.x(基于Netty,也是非阻塞的,支持长连接)

1)SpringCloud Gateway

Spring Cloud Gateway 是Spring Cloud的一个全新的API网关项目,目的是为了替换掉Zuul1。Gateway可以与Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等组件配合使用,实现路由转发、负载均衡、熔断等功能,并且Gateway还内置了限流过滤器,实现了限流的功能。

Gateway基于Spring 5、Spring boot 2和Reactor构建,使用Netty作为运行时环境,比较完美的支持异步非阻塞编程。Netty使用非阻塞的IO,线程处理模型建立在主从Reactors多线程模型上。其中Boss Group轮询到新连接后与Client建立连接,生成NioSocketChannel,将channel绑定到Worker;Worker Group轮询并处理Read、Write事件。

 
 

posted on 2022-09-19 18:18  书梦一生  阅读(295)  评论(0编辑  收藏  举报

导航