过滤器优先级
如图所示,数字越小,执行的优先级就越高:
图表展示出来就是:
过滤器 | order | 描述 | 类型 |
---|---|---|---|
ServletDetectionFilter | -3 | 检测请求是用 DispatcherServlet还是 ZuulServlet | pre |
Servlet30WrapperFilter | -2 | 在Servlet 3.0 下,包装 requests | pre |
FormBodyWrapperFilter | -1 | 解析表单数据 | pre |
SendErrorFilter | 0 | 如果中途出现错误 | error |
DebugFilter | 1 | 设置请求过程是否开启debug | pre |
PreDecorationFilter | 5 | 根据uri决定调用哪一个route过滤器 | pre |
RibbonRoutingFilter | 10 | 如果写配置的时候用ServiceId则用这个route过滤器,该过滤器可以用Ribbon 做负载均衡,用hystrix做熔断 | route |
SimpleHostRoutingFilter | 100 | 如果写配置的时候用url则用这个route过滤 | route |
SendForwardFilter | 500 | 用RequestDispatcher请求转发 | route |
SendResponseFilter | 1000 | 用RequestDispatcher请求转发 | post |
为了能够更清晰地知道底层是怎么完成这一系列流程的,我们通过编写一个自定义的过滤器来测试。从我们上一篇博客Spring Cloud集群中使用Zuul(十七) 底部拿到源码。
依次运行四个项目的*App启动类,启动四个项目。
在eureka-zuul网关项目中,在com.init.springCloud包下创建filter包,新建SelfDefineFilter类,去继承Zuul框架的ZuulFilter类,实现内部方法。在图上的Route阶段,三种路由方式都继承了这个ZuulFilter:
package com.init.springCloud.filter;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import com.netflix.zuul.ZuulFilter;
public class SelfDefineFilter extends ZuulFilter {
@Override
public Object run() {
System.out.println("执行自定义过滤器");
return null;
}
@Override
public boolean shouldFilter() {
//是否执行当前过滤器
return true;
}
@Override
public int filterOrder() {
//执行顺序
return 5;
}
@Override
public String filterType() {
//过滤器执行阶段的类型
return FilterConstants.ROUTE_TYPE;
}
}
按照Route阶段的执行顺序,我们规定自定义的路由过滤器执行顺序,在Route阶段比优先级最高的RibbonRoutingFilter还高(RibbonRoutingFilter为10,自定义路由过滤器为5)。
之后,编写一个配置类SelfDefineConf.class,把这个自定义的路由过滤器当成Spring的Bean组件注入进去:
package com.init.springCloud.filter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SelfDefineConf {
@Bean
public SelfDefineFilter getFilter(){
return new SelfDefineFilter();
}
}
重新启动eureka-zuul项目,任意访问一个接口,譬如这里访问:http://localhost:9090/consumer/router或者http://localhost:9090/users/router,都可以看到Zuul内部执行了我们自定义的路由过滤器:
过滤器的动态加载
在一个大型项目中,由于网关接手了大部分的请求,所以它是一个不能随随便便就停止的项目,当我们涉及到有新的路由规则上线,又不能停止网关项目的时候,就可以使用过滤器的动态加载来完成这个操作。使用groovy可以实现在不停止网关项目的同时,动态加载我们自定义的过滤器,在eureka-zuul的pom.xml中加入groovy相关依赖:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.15</version>
</dependency>
接着在ZuulApp.class启动类里面配置zuul的动态加载过滤器规则:
@PostConstruct
public void zuulInit(){
FilterLoader.getInstance().setCompiler(new GroovyCompiler());
//过滤器的路径
String scriptRoot = System.getProperty("zuul.filter.root","groovy/filters");
//定时器时间,5秒刷新一次
String refreshInterval = System.getProperty("zuul.filter.refreshInterval","5");
if(scriptRoot.length() > 0){
scriptRoot += File.separator;
}
try {
FilterFileManager.setFilenameFilter(new GroovyFileFilter());
FilterFileManager.init(Integer.parseInt(refreshInterval),
scriptRoot+"pre", scriptRoot+"route", scriptRoot+"post");
} catch (Exception e) {
e.printStackTrace();
}
}
我们设置了过滤器读取的位置是groovy.filters,所以我们在src/main/java目录下创建groovy.filters包,并在这个包下创建三个包:pre、route、post(groovy只会读取这三个包下面的内容,源代码中把文件放在了外面的包里,不被读取)。之后启动项目。
接下来编写一个文件DynamicFilter.groovy,内容和我们上面自定义的过滤器类似,没有大的修改:
package groovy.filters;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import com.netflix.zuul.ZuulFilter;
class DynamicFilter extends ZuulFilter {
@Override
public Object run() {
System.out.println("===>这里是用groovy实现的动态加载过滤器");
return null;
}
@Override
public boolean shouldFilter() {
//是否执行当前过滤器
return true;
}
@Override
public int filterOrder() {
//执行顺序
return 3;
}
@Override
public String filterType() {
//过滤器执行阶段的类型
return FilterConstants.ROUTE_TYPE;
}
}
在项目启动之后,我们依然访问一下:http://localhost:9090/users/router,可以看到控制台使用了我们自定义的路由规则。接着我们把这个DynamicFilter.groovy文件拖拽到groovy.filters.route包下,等待5秒左右,再次访问:http://localhost:9090/users/router,就可以看到zuul已经动态加载了我们配置的过滤器了:
@EnableZuulServer
和@EnableZuulProxy对应的还有一个@EnableZuulServer,功能相差不多,相当于是一个简化版本,缺失了灰色字体标注的项目。
最后,大家有什么不懂的或者其他需要交流的内容,也可以进入我的QQ讨论群一起讨论:654331206
Spring Cloud系列:
Spring Cloud服务管理框架Eureka简单示例(三)