Netflix之Zuul的进阶应用(十八)

过滤器优先级

如图所示,数字越小,执行的优先级就越高:

图表展示出来就是:

过滤器 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,功能相差不多,相当于是一个简化版本,缺失了灰色字体标注的项目。

源码点击这里

猜你喜欢

转载自blog.csdn.net/mrspirit/article/details/80533995