netflix zuul
introduction
zuul
用来提供动态路由、监控、授权、安全、调度等等的边缘服务(edge service)
ZuulFilter
ZuulFilter
是Zuul
中核心组件,通过继承该抽象类,覆写几个关键方法达到自定义调度请求的作用,这里filter不是java web
中的filter,不要混淆.
new ZuulFilter() {
@Override
public int filterOrder() {
return 0;
}
@Override
public String filterType() {
return null;
}
@Override
public boolean shouldFilter() {
return false;
}
@Override
public Object run() {
return null;
}
}
filterOrder
:filter执行顺序,通过数字指定shouldFilter
:filter是否需要执行true
执行false
不执行run
: filter具体逻辑filterType
:filter类型,分为以下几种
pre
:请求执行之前filterroute
: 处理请求,进行路由post
: 请求处理完成后执行的filtererror
:出现错误时执行的filter
quick start
直接给出一个简单demo,通过demo代码再具体解析
package com.lkl.springcloud.zuul;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.ContextLifecycleFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.filters.FilterRegistry;
import com.netflix.zuul.http.ZuulServlet;
import com.netflix.zuul.monitoring.MonitoringHelper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* Created by liaokailin on 16/5/24.
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
@Component
public static class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
MonitoringHelper.initMocks();
initJavaFilters();
}
private void initJavaFilters() {
final FilterRegistry r = FilterRegistry.instance();
r.put("javaPreFilter", new ZuulFilter() {
@Override
public int filterOrder() {
return 50000;
}
@Override
public String filterType() {
return "pre";
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
System.out.println("running javaPreFilter");
RequestContext.getCurrentContext().set("name", "liaokailin");
return null;
}
});
r.put("javaRoutingFilter", new ZuulFilter() {
@Override
public int filterOrder() {
return 50000;
}
@Override
public String filterType() {
return "route";
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
System.out.println("running javaRoutingFilter");
try {
RequestContext.getCurrentContext().getResponse().sendRedirect("https://www.xnip.cn");
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
});
r.put("javaPostFilter", new ZuulFilter() {
@Override
public int filterOrder() {
return 50000;
}
@Override
public String filterType() {
return "post";
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
System.out.println("running javaPostFilter");
System.out.println(RequestContext.getCurrentContext().get("name").toString());
return null;
}
});
}
}
@Bean
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet());
servlet.addUrlMappings("/test");
return servlet;
}
@Bean
public FilterRegistrationBean contextLifecycleFilter() {
FilterRegistrationBean filter = new FilterRegistrationBean(new ContextLifecycleFilter());
filter.addUrlPatterns("/*");
return filter;
}
}
servlet注册
通过ServletRegistrationBean
构造ZuulServlet
,该Servlet用以进行filter
执行调度以及监控等等操作 访问 http://localhost:8080/test 进入该servlet
filter注册
通过FilterRegistrationBean
进行filter注册,ContextLifecycleFilter
的核心功能是为了清除RequestContext
; 请求上下文通过ThreadLocal
存储,因此需要在请求完成后删除该对象。
CommandLineRunner
CommandLineRunner
接口很简单,知道spring boot
的知道其功能,在工程启动后会执行对应run
方法:
MonitoringHelper.initMocks();
启动监控,这个再后续文章中具体再说 initJavaFilters()
方法中注册三种类型filter
RequestContext
RequestContext
在zuul
有很重作用,在不同组件传递数据都是通过它来实现的
启动工程后访问 http://localhost:8080/test 可以看到后台答应消息,页面也会定位到我的博客首页。
zuul执行流程
借用官网的一张图片
通过图片可以清晰看出执行过程,在微服务中后端各种引用,利用zuul进行合理调用还是很有必要的,例如 负载、限流、监控、安全等等功能。
zuul with groovy
为了动态修改filter,zuul
利用groovy
,它是基于jvm的语言,语法简单而且和java很类似,可以简单的理解为在java语法上进行拓展,但groovy是可以动态加载的,应用发布到线上后可以在不重启情况下对业务逻辑进行修改。
为了简单起见,下面创建一个groovy filter
import com.netflix.zuul.ZuulFilter
import com.netflix.zuul.context.RequestContext
import javax.servlet.http.HttpServletRequest
class PreRequest extends ZuulFilter{
@Override
String filterType() {
return "pre"
}
@Override
int filterOrder() {
return 1000
}
@Override
boolean shouldFilter() {
return true
}
@Override
Object run() {
HttpServletRequest req = RequestContext.currentContext.request as HttpServletRequest
Iterator headerIt = req.getHeaderNames().iterator()
while (headerIt.hasNext()) {
String name = (String) headerIt.next()
String value = req.getHeader(name)
println("header: " + name + ":" + value)
}
return null
}
}
创建一个pre
类型的filter,在run
方法中获取HttpServletRequest
然后答应header信息
在代码中加入groovy编译器,间隔10秒扫描一次groovy文件,其代码如下:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.4</version>
</dependency>
FilterLoader.getInstance().setCompiler(new GroovyCompiler());
try {
FilterFileManager.setFilenameFilter(new GroovyFileFilter());
FilterFileManager.init(10,"/Users/liaokailin/code/ieda/springcloud/myzuul/src/main/java/com/lkl/springcloud/zuul/filters/groovy/pre");
} catch (Exception e) {
throw new RuntimeException(e);
}
这里groovy文件通过绝对路径指定,如果是实际开发中,可以通过db去存储groovy文件。
启动应用后再访问该工程,可以发现header信息全部答应出来。