springCloud学习05之api网关服务zuul过滤器filter

前面学习了zuul的反向代理、负载均衡、fallback回退。这张学习写过滤器filter,做java web开发的对filter都不陌生,那就是客户端(如浏览器)发起请求的时候,都先经过过滤器filter做一些相关的校验或业务判断(如登录、权限等),zuul也同样提供了过滤器功能。只要继承ZuulFilter类即可。

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

然而实际上,路由功能在真正运行时,它的路由映射和请求转发都是由几个不同的过滤器完成的。其中,路由映射主要通过pre类型的过滤器完成,它将请求路径与配置的路由规则进行匹配,以找到需要转发的目标地址;而请求转发的部分则是由route类型的过滤器来完成,对pre类型过滤器获得的路由地址进行转发。

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

继承ZuulFilter.java,需要实现一些方法

package com.fei.springcloud.filter;

import com.netflix.zuul.ZuulFilter;

public class TestFilter extends ZuulFilter{

@Override
public boolean shouldFilter() {
return false;
}

@Override
public Object run() {
return null;
}

@Override
public String filterType() {
return null;
}

@Override
public int filterOrder() {
return 0;
}

}

它们各自的含义与功能总结如下:

filterType:该函数需要返回一个字符串来代表过滤器的类型,而这个类型就是在HTTP请求过程中定义的各个阶段。在Zuul中默认定义了四种不同生命周期的过滤器类型,具体如下:
pre:可以在请求被路由之前调用。
routing:在路由请求时候被调用。
post:在routing和error过滤器之后被调用。
error:处理请求时发生错误时被调用。
filterOrder:通过int值来定义过滤器的执行顺序,数值越小优先级越高。
shouldFilter:返回一个boolean类型来判断该过滤器是否要执行。我们可以通过此方法来指定过滤器的有效范围。
run:过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑,来确定是否要拦截当前的请求,不对其进行后续的路由,或是在请求路由返回结果之后,对处理结果做一些加工等。

从上图中,我们可以看到,当外部HTTP请求到达API网关服务的时候,首先它会进入第一个阶段pre,在这里它会被pre类型的过滤器进行处理,该类型的过滤器主要目的是在进行请求路由之前做一些前置加工,比如请求的校验等。

在完成了pre类型的过滤器处理之后,请求进入第二个阶段routing,也就是之前说的路由请求转发阶段,请求将会被routing类型过滤器处理,这里的具体处理内容就是将外部请求转发到具体服务实例上去的过程,当服务实例将请求结果都返回之后,routing阶段完成,请求进入第三个阶段post,此时请求将会被post类型的过滤器进行处理,这些过滤器在处理的时候不仅可以获取到请求信息,还能获取到服务实例的返回信息,所以在post类型的过滤器中,我们可以对处理结果进行一些加工或转换等内容。

另外,还有一个特殊的阶段error,该阶段只有在上述三个阶段中发生异常的时候才会触发,但是它的最后流向还是post类型的过滤器,因为它需要通过post过滤器将最终结果返回给请求客户端(实际实现上还有一些差别)

Zuul自带的核心过滤器

在spring-cloud-netflix-core-XXX.jar架包下

如上图所示,在默认启用的过滤器中包含了三种不同生命周期的过滤器,这些过滤器都非常重要,可以帮助我们理解Zuul对外部请求处理的过程,以及帮助我们如何在此基础上扩展过滤器去完成自身系统需要的功能。下面,我们将逐个地对这些过滤器做一些详细的介绍:

pre过滤器
ServletDetectionFilter:它的执行顺序为-3,是最先被执行的过滤器。该过滤器总是会被执行,主要用来检测当前请求是通过Spring的DispatcherServlet处理运行,还是通过ZuulServlet来处理运行的。

它的检测结果会以布尔类型保存在当前请求上下文的isDispatcherServletRequest参数中,这样在后续的过滤器中,我们就可以通过RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法判断它以实现做不同的处理。

一般情况下,发送到API网关的外部请求都会被Spring的DispatcherServlet处理,除了通过/zuul/路径访问的请求会绕过DispatcherServlet,被ZuulServlet处理,主要用来应对处理大文件上传的情况。另外,对于ZuulServlet的访问路径/zuul/,我们可以通过zuul.servletPath参数来进行修改。

Servlet30WrapperFilter:它的执行顺序为-2,是第二个执行的过滤器。目前的实现会对所有请求生效,主要为了将原始的HttpServletRequest包装成Servlet30RequestWrapper对象。
FormBodyWrapperFilter:它的执行顺序为-1,是第三个执行的过滤器。

该过滤器仅对两种类请求生效,第一类是Content-Type为application/x-www-form-urlencoded的请求,第二类是Content-Type为multipart/form-data并且是由Spring的DispatcherServlet处理的请求(用到了ServletDetectionFilter的处理结果)。

而该过滤器的主要目的是将符合要求的请求体包装成FormBodyRequestWrapper对象。
DebugFilter:它的执行顺序为1,是第四个执行的过滤器。

该过滤器会根据配置参数zuul.debug.request和请求中的debug参数来决定是否执行过滤器中的操作。而它的具体操作内容则是将当前的请求上下文中的debugRouting和debugRequest参数设置为true。

由于在同一个请求的不同生命周期中,都可以访问到这两个值,所以我们在后续的各个过滤器中可以利用这两值来定义一些debug信息,这样当线上环境出现问题的时候,可以通过请求参数的方式来激活这些debug信息以帮助分析问题。

另外,对于请求参数中的debug参数,我们也可以通过zuul.debug.parameter来进行自定义。
PreDecorationFilter:它的执行顺序为5,是pre阶段最后被执行的过滤器。该过滤器会判断当前请求上下文中是否存在forward.to和serviceId参数,如果都不存在,那么它就会执行具体过滤器的操作(如果有一个存在的话,说明当前请求已经被处理过了,因为这两个信息就是根据当前请求的路由信息加载进来的)。

而它的具体操作内容就是为当前请求做一些预处理,比如:进行路由规则的匹配、在请求上下文中设置该请求的基本信息以及将路由匹配结果等一些设置信息等,这些信息将是后续过滤器进行处理的重要依据,我们可以通过RequestContext.getCurrentContext()来访问这些信息。

另外,我们还可以在该实现中找到一些对HTTP头请求进行处理的逻辑,其中包含了一些耳熟能详的头域,比如:X-Forwarded-Host、X-Forwarded-Port。

另外,对于这些头域的记录是通过zuul.addProxyHeaders参数进行控制的,而这个参数默认值为true,所以Zuul在请求跳转时默认地会为请求增加X-Forwarded-*头域,包括:X-Forwarded-Host、X-Forwarded-Port、X-Forwarded-For、X-Forwarded-Prefix、X-Forwarded-Proto。

我们也可以通过设置zuul.addProxyHeaders=false关闭对这些头域的添加动作。

route过滤器
RibbonRoutingFilter:它的执行顺序为10,是route阶段第一个执行的过滤器。该过滤器只对请求上下文中存在serviceId参数的请求进行处理,即只对通过serviceId配置路由规则的请求生效。而该过滤器的执行逻辑就是面向服务路由的核心,它通过使用Ribbon和Hystrix来向服务实例发起请求,并将服务实例的请求结果返回。
SimpleHostRoutingFilter:它的执行顺序为100,是route阶段第二个执行的过滤器。该过滤器只对请求上下文中存在routeHost参数的请求进行处理,即只对通过url配置路由规则的请求生效。而该过滤器的执行逻辑就是直接向routeHost参数的物理地址发起请求,从源码中我们可以知道该请求是直接通过httpclient包实现的,而没有使用Hystrix命令进行包装,所以这类请求并没有线程隔离和断路器的保护。
SendForwardFilter:它的执行顺序为500,是route阶段第三个执行的过滤器。该过滤器只对请求上下文中存在forward.to参数的请求进行处理,即用来处理路由规则中的forward本地跳转配置。
post过滤器
SendErrorFilter:它的执行顺序为0,是post阶段第一个执行的过滤器。该过滤器仅在请求上下文中包含error.status_code参数(由之前执行的过滤器设置的错误编码)并且还没有被该过滤器处理过的时候执行。而该过滤器的具体逻辑就是利用请求上下文中的错误信息来组织成一个forward到API网关/error错误端点的请求来产生错误响应。
SendResponseFilter:它的执行顺序为1000,是post阶段最后执行的过滤器。该过滤器会检查请求上下文中是否包含请求响应相关的头信息、响应数据流或是响应体,只有在包含它们其中一个的时候就会执行处理逻辑。而该过滤器的处理逻辑就是利用请求上下文的响应信息来组织需要发送回客户端的响应内容。

代码实战,完整代码在github上

写2个pre类型的filter,1个post类型的filter.如果在第一个pre过滤器验证就失败了,则后面的过滤器不需要执行了。

TestPre01Filter.java

package com.fei.springcloud.filter;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

/**
* 第一个pre类型的filter,prefilter01=true才能通过
* @author Jfei
*
*/
public class TestPre01Filter extends ZuulFilter{

/**
* 是否应该执行该过滤器,如果是false,则不执行该filter
*/
@Override
public boolean shouldFilter() {
return true;
}

/**
* 过滤器类型
* 顺序: pre ->routing -> post ,以上3个顺序出现异常时都可以触发error类型的filter
*/
@Override
public String filterType() {

return FilterConstants.PRE_TYPE;
}

/**
* 同filterType类型中,order值越大,优先级越低
*/
@Override
public int filterOrder() {

return 1;
}
/**
* 执行业务操作,可执行sql,nosql等业务
*/
@Override
public Object run() {

RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();

String prefilter01 = request.getParameter("prefilter01");
System.out.println("执行pre01Filter .....prefilter01=" + prefilter01 );

//如果用户名和密码都正确,则继续执行下一个filter
if("true".equals(prefilter01) ){
ctx.setSendZuulResponse(true);//会进行路由,也就是会调用api服务提供者
ctx.setResponseStatusCode(200);
ctx.set("isOK",true);//可以把一些值放到ctx中,便于后面的filter获取使用
}else{
ctx.setSendZuulResponse(false);//不需要进行路由,也就是不会调用api服务提供者
ctx.setResponseStatusCode(401);
ctx.set("isOK",false);//可以把一些值放到ctx中,便于后面的filter获取使用
//返回内容给客户端
ctx.setResponseBody("{\"result\":\"pre01Filter auth not correct!\"}");// 返回错误内容
}

return null;
}

}
这是第一个自定义的pre类型filter,说以shuldFilter()为true,也就是必须执行
TestPre02Filter.java

package com.fei.springcloud.filter;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

/**
* prefilter02 校验 prefilter02=true才能通过
* @author Jfei
*
*/
public class TestPre02Filter extends ZuulFilter{

/**
* 是否应该执行该过滤器,如果是false,则不执行该filter
*/
@Override
public boolean shouldFilter() {
//上一个filter设置该值
return RequestContext.getCurrentContext().getBoolean("isOK");
}

/**
* 过滤器类型
* 顺序: pre ->routing -> post ,以上3个顺序出现异常时都可以触发error类型的filter
*/
@Override
public String filterType() {

return FilterConstants.PRE_TYPE;
}

/**
* 同filterType类型中,order值越大,优先级越低
*/
@Override
public int filterOrder() {

return 2;
}

@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();

String prefilter02 = request.getParameter("prefilter02");
System.out.println("执行pre02Filter .....prefilter02=" + prefilter02 );

//如果用户名和密码都正确,则继续执行下一个filter
if("true".equals(prefilter02) ){
ctx.setSendZuulResponse(true);//会进行路由,也就是会调用api服务提供者
ctx.setResponseStatusCode(200);
ctx.set("isOK",true);//可以把一些值放到ctx中,便于后面的filter获取使用
}else{
ctx.setSendZuulResponse(false);//不需要进行路由,也就是不会调用api服务提供者
ctx.setResponseStatusCode(401);
ctx.set("isOK",false);//可以把一些值放到ctx中,便于后面的filter获取使用
//返回内容给客户端
ctx.setResponseBody("{\"result\":\"pre02Filter auth not correct!\"}");// 返回错误内容
}

return null;
}

}

TestPostFilter.java
package com.fei.springcloud.filter;

import javax.servlet.http.HttpServletRequest;

import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
/**
*
* post类型的filter,post=true才能通过
*
*/
public class TestPostFilter extends ZuulFilter{

/**
* 是否应该执行该过滤器,如果是false,则不执行该filter
*/
@Override
public boolean shouldFilter() {
//上一个filter设置该值
return RequestContext.getCurrentContext().getBoolean("isOK");
}

/**
* 过滤器类型
* 顺序: pre ->routing -> post ,以上3个顺序出现异常时都可以触发error类型的filter
*/
@Override
public String filterType() {

return FilterConstants.ROUTE_TYPE;
}

/**
* 同filterType类型中,order值越大,优先级越低
*/
@Override
public int filterOrder() {

return 1;
}

@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();

String post = request.getParameter("post");
System.out.println("执行postFilter .....post=" + post );

//如果用户名和密码都正确,则继续执行下一个filter
if("true".equals(post) ){
ctx.setSendZuulResponse(true);//会进行路由,也就是会调用api服务提供者
ctx.setResponseStatusCode(200);
ctx.set("isOK",true);//可以把一些值放到ctx中,便于后面的filter获取使用
}else{
ctx.setSendZuulResponse(false);//不需要进行路由,也就是不会调用api服务提供者
ctx.setResponseStatusCode(401);
ctx.set("isOK",false);//可以把一些值放到ctx中,便于后面的filter获取使用
//返回内容给客户端
ctx.setResponseBody("{\"result\":\"post auth not correct!\"}");// 返回错误内容
}

return null;
}

}

启动类,对filter进行注册
package com.fei.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;

import com.fei.springcloud.filter.TestPre02Filter;
import com.fei.springcloud.filter.TestPostFilter;
import com.fei.springcloud.filter.TestPre01Filter;

@EnableZuulProxy
@SpringCloudApplication
public class ZuulFilterApplication {

public static void main(String[] args) {

SpringApplication.run(ZuulFilterApplication.class, args);
}

@Bean
public TestPre01Filter testPre01Filter(){
return new TestPre01Filter();
}

@Bean
public TestPre02Filter testPre02Filter(){
return new TestPre02Filter();
}

@Bean
public TestPostFilter testPostFilter(){
return new TestPostFilter();
}

}

启动eureka-server,eureka-api(前面学习过,也有完整代码了),然后启动Zuul-filter,
浏览器请求 http://127.0.0.1:8888/user-api/user/find
http://127.0.0.1:8888/user-api/user/find?prefilter01=true

http://127.0.0.1:8888/user-api/user/find?prefilter01=true&prefilter02=true

对比页面看到的内容和控制台打印的日志,会发现如果pre的校验都通不过的时候,api微服务就没打印了,说明没调用api微服务,post filter也没执行
---------------------
作者:夢_殤
来源:CSDN
原文:https://blog.csdn.net/dream_broken/article/details/77197585
版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/dauber/p/9830584.html

时间: 2024-11-05 21:45:24

springCloud学习05之api网关服务zuul过滤器filter的相关文章

干货分享微服务spring-cloud(6.Api网关服务zuul)

Spring cloud zuul对Netflix zuul进行了封装实现 新建spring boot项目demo-springcloud-api-gateway,创建启动类ZuulApplication,@EnableZuulProxy启动网关代理的配置,它也是组合注解,启用了服务注册发现和熔断器 项目关键依赖spring-cloud-starter-zuul对网关支持 配置文件application.properties,路由配置主要有传统路由和服务路由,传统路由就是ip和端口这种,服务路由

Spring Cloud学习系列第五篇【API网关服务】

这篇随笔接着学习微服务中一个比较重要的组件API网关服务.当我们微服务架构完成后最终是要提供给外部访问的,于是我们需要一个统一的访问入口,能隐藏我们内部服务URL细节,这就有点像局域网里那个网关的概念了,这是API网关服务就应运而生了.API网关作用有能为实现请求路由.负载均衡.校验过滤等基础功能,还能实现请求转发的熔断机制.服务集合等高级功能.补充下通常我们对外服务统一入口可以采用F5.Nginx等方式也能实现前面的请求路由与负载均衡,但是要实现后面功能了F5.Nginx就无能为力了吧,这就是

Spring Cloud(五):API网关服务——Spring Cloud Zuul

通过前面的介绍,我们可以使用Spring Boot进行微服务开发,使用Spring Cloud Eureka实现注册中心以及微服务的注册和发现,使用Spring Cloud Ribbon实现服务间的负载均衡,使用Spring Cloud Hystrix实现线程隔离以及断路器功能.但是实际应用中这样的架构无疑增加了开发成本以及运维难度,而且后期重构难度也很大.为了解决以上各种问题,需要使用API 网关的方式.API 网关是一个服务器,它是进入一个系统的唯一节点,封装了内部系统的架构,并且提供了AP

Ocelot实现API网关服务

NET Core微服务之基于Ocelot实现API网关服务 https://www.cnblogs.com/edisonchou/p/api_gateway_ocelot_foundation_01.html 一.啥是API网关? API 网关一般放到微服务的最前端,并且要让API 网关变成由应用所发起的每个请求的入口.这样就可以明显的简化客户端实现和微服务应用程序之间的沟通方式.以前的话,客户端不得不去请求微服务A(假设为Customers),然后再到微服务B(假设为Orders),然后是微服

007API网关服务Zuul

001.POM配置 和普通Spring Boot工程相比,增加了Eureka Client.Zuul依赖和Spring Cloud依赖管理 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <

.NET Core微服务之基于Ocelot实现API网关服务(续)

一.负载均衡与请求缓存 1.1 负载均衡 为了验证负载均衡,这里我们配置了两个Consul Client节点,其中ClientService分别部署于这两个节点内(192.168.80.70与192.168.80.71). 为了更好的展示API Repsonse来自哪个节点,我们更改一下返回值: [Route("api/[controller]")] public class ValuesController : Controller { // GET api/values [Http

基于.NET CORE微服务框架 -Api网关服务管理

1.前言 经过10多天的努力,surging 网关已经有了大致的雏形,后面还会持续更新完善,请大家持续关注研发的动态 最近也更新了surging新的版本 更新内容: 1. 扩展Zookeeper封装2. 增加服务元数据3. 增加API网关 开源地址:https://github.com/dotnetcore/surging 2.软件环境 IDE:Visual Studio 2017 15.3 Preview ,vscode 框架:.NET core 2.0 依赖程序:Zookeepe.Rabbi

基于spring-cloud的微服务(4)API网关zuul

API网关是微服务架构中的很重要的一个部分,内部有多个不同的服务提供给外部来使用,API网关可以对外做统一的入口,也可以在网关上做协议转换,权限控制和请求统计和限流等其他的工作 spring-cloud封装了Netflix提供的开源的API网关实现zuul,我们可以很方便地启动一个zuul网关的实例,并支持向eureka进行注册,并对在eureka上已经注册的服务进行代理 使用IDEA的spring initializer来创建一个zuul项目 填写相关的group类型等信息,选择使用gradl

SpringCloud学习记录——网关(Zuul)

在微服务中网关的作用: 1.接收请求——与一般访问相同 2.转发请求 3.请求过滤 网关作用图示: 从上图可以看出,网关的作用实际上是对原始的请求方式插入了一层:在请求中间加入了一层网关,这样使得外部的所有请求都导向网关,再由网关来转发给具体服务处理. 加入网关的优势: 1.请求统一:原本的请求方式可能会因为访问的服务不同,出现访问地址也不同的问题,插入网关之后,访问地址统一指向网关:对于请求方而言,只需要记住一个访问地址就可以了,省事了: 2.通用功能的统一处理:与请求相关的各种操作都可以在网