zull架构图
首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的zuulfilter共享。
zuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的zuulfilter的管理器。
FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。
有了这些filter之后,zuulservelet首先执行的Pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。
Zuul能干什么
身份验证和安全 - 识别每个资源的身份验证要求并拒绝不满足的请求
审查和监测 - 跟踪边缘的有意义的数据和统计数据以便我们准确地了解生产运行情况
动态路由 - 根据需要将请求动态路由到不同的后端集群
压力测试 - 逐渐增加到集群的流量以衡量性能
负载分配 - 为每种类型的请求分配容量并删除超出限制的请求
静态响应处理 - 直接在边缘构建一些响应而不是将它们转发到内部集群
RequestContext:用于在过滤器之间传递上下文,如:请求路由到哪里、错误、HttpServletRequest、HttpServletResponse,数据保存在每个请求的ThreadLocal中。
zull主要做限流(RateLimit)、路由、拦截处理
源码先从@EnableZuulProxy标签开始链接进去查看
@EnableCircuitBreaker
@EnableDiscoveryClient
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({ZuulProxyConfiguration.class})
public @interface EnableZuulProxy {
}
自带了断路由@EnableCircuitBreaker,服务发现@EnableDiscoveryClient的功能再看Import({ZuulProxyConfiguration.class}) 查看ZuulProxyConfiguration配置中具体的代码
@Import({RestClientRibbonConfiguration.class, OkHttpRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class ZuulProxyConfiguration extends ZuulConfiguration {
。。。。
@Bean
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties, proxyRequestHelper);
}
@Bean
public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper, RibbonCommandFactory<?> ribbonCommandFactory) {
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
return filter;
}
@Bean
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
return new SimpleHostRoutingFilter(helper, zuulProperties);
}
可以看到配置了很多Filter,再看继承的父类ZuulConfiguration中
@Configuration
@EnableConfigurationProperties({ZuulProperties.class})
@ConditionalOnClass({ZuulServlet.class})
@Import({ServerPropertiesAutoConfiguration.class})
public class ZuulConfiguration {
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
@Autowired(
required = false
)
private ErrorController errorController;
public ZuulConfiguration() {
}
@Bean
public HasFeatures zuulFeature() {
return HasFeatures.namedFeature("Zuul (Simple)", ZuulConfiguration.class);
}
@Bean
@Primary
public CompositeRouteLocator primaryRouteLocator(Collection<RouteLocator> routeLocators) {
return new CompositeRouteLocator(routeLocators);
}
。。。
@Bean
@ConditionalOnMissingBean(
name = {"zuulServlet"}
)
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(), new String[]{this.zuulProperties.getServletPattern()});
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}
。。。
@Configuration
protected static class ZuulFilterConfiguration {
@Autowired
private Map<String, ZuulFilter> filters;
protected ZuulFilterConfiguration() {
}
@Bean
public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory, TracerFactory tracerFactory) {
FilterLoader filterLoader = FilterLoader.getInstance();
FilterRegistry filterRegistry = FilterRegistry.instance();
return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
}
}
可以看到引入了ZuulServlet,以及ZuulFilterConfiguration,ZuulFilterInitializer,先看ZuulServlet的源码
public class ZuulServlet extends HttpServlet {
private static final long serialVersionUID = -3374242278843351500L;
private ZuulRunner zuulRunner;
public ZuulServlet() {
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true");
this.zuulRunner = new ZuulRunner(bufferReqs);
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
this.preRoute();
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
}
try {
this.route();
} catch (ZuulException var13) {
this.error(var13);
this.postRoute();
return;
}
try {
this.postRoute();
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
void postRoute() throws ZuulException {
this.zuulRunner.postRoute();
}
void route() throws ZuulException {
this.zuulRunner.route();
}
void preRoute() throws ZuulException {
this.zuulRunner.preRoute();
}
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
this.zuulRunner.init(servletRequest, servletResponse);
}
ZuulServlet extends HttpServlet ,同时service中进行了preRoute,route,postRoute的处理,均是交给了ZuulRunner进行执行,看ZuulRunner源码
public class ZuulRunner {
private boolean bufferRequests;
public ZuulRunner() {
this.bufferRequests = true;
}
public ZuulRunner(boolean bufferRequests) {
this.bufferRequests = bufferRequests;
}
public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
RequestContext ctx = RequestContext.getCurrentContext();
if (this.bufferRequests) {
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
}
ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}
public void postRoute() throws ZuulException {
FilterProcessor.getInstance().postRoute();
}
public void route() throws ZuulException {
FilterProcessor.getInstance().route();
}
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
public void error() {
FilterProcessor.getInstance().error();
}
可以看到ZuulRunner初始化时从RequestContext中获取内容,执行时交给FilterProcessor处理,增加了error方法,再看FilterProcessor
public class FilterProcessor {
static FilterProcessor INSTANCE = new FilterProcessor();
protected static final Logger logger = LoggerFactory.getLogger(FilterProcessor.class);
private FilterUsageNotifier usageNotifier = new FilterProcessor.BasicFilterUsageNotifier();
public FilterProcessor() {
}
public void postRoute() throws ZuulException {
try {
this.runFilters("post");
} catch (ZuulException var2) {
throw var2;
} catch (Throwable var3) {
throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + var3.getClass().getName());
}
}
public void error() {
try {
this.runFilters("error");
} catch (Throwable var2) {
logger.error(var2.getMessage(), var2);
}
}
public void route() throws ZuulException {
try {
this.runFilters("route");
} catch (ZuulException var2) {
throw var2;
} catch (Throwable var3) {
throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + var3.getClass().getName());
}
}
public void preRoute() throws ZuulException {
try {
this.runFilters("pre");
} catch (ZuulException var2) {
throw var2;
} catch (Throwable var3) {
throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
}
}
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for(int i = 0; i < list.size(); ++i) {
ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
Object result = this.processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= (Boolean)result;
}
}
}
return bResult;
}
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
String metricPrefix = "zuul.filter-";
long execTime = 0L;
String filterName = "";
try {
long ltime = System.currentTimeMillis();
filterName = filter.getClass().getSimpleName();
RequestContext copy = null;
Object o = null;
Throwable t = null;
if (bDebug) {
Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
copy = ctx.copy();
}
ZuulFilterResult result = filter.runFilter();
ExecutionStatus s = result.getStatus();
execTime = System.currentTimeMillis() - ltime;
switch(s) {
case FAILED:
t = result.getException();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
break;
case SUCCESS:
o = result.getResult();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
if (bDebug) {
Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
Debug.compareContextState(filterName, copy);
}
}
if (t != null) {
throw t;
} else {
this.usageNotifier.notify(filter, s);
return o;
}
} catch (Throwable var15) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + var15.getMessage());
}
this.usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (var15 instanceof ZuulException) {
throw (ZuulException)var15;
} else {
ZuulException ex = new ZuulException(var15, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}
从FilterLoader中获取filter集合,然后进行执行,
public class FilterLoader {
static final FilterLoader INSTANCE = new FilterLoader();
private static final Logger LOG = LoggerFactory.getLogger(FilterLoader.class);
private final ConcurrentHashMap<String, Long> filterClassLastModified = new ConcurrentHashMap();
private final ConcurrentHashMap<String, String> filterClassCode = new ConcurrentHashMap();
private final ConcurrentHashMap<String, String> filterCheck = new ConcurrentHashMap();
private final ConcurrentHashMap<String, List<ZuulFilter>> hashFiltersByType = new ConcurrentHashMap();
private FilterRegistry filterRegistry = FilterRegistry.instance();
static DynamicCodeCompiler COMPILER;
static FilterFactory FILTER_FACTORY = new DefaultFilterFactory();
public FilterLoader() {
}
public void setCompiler(DynamicCodeCompiler compiler) {
COMPILER = compiler;
}
public void setFilterRegistry(FilterRegistry r) {
this.filterRegistry = r;
}
public void setFilterFactory(FilterFactory factory) {
FILTER_FACTORY = factory;
}
public static FilterLoader getInstance() {
return INSTANCE;
}
public ZuulFilter getFilter(String sCode, String sName) throws Exception {
if (this.filterCheck.get(sName) == null) {
this.filterCheck.putIfAbsent(sName, sName);
if (!sCode.equals(this.filterClassCode.get(sName))) {
LOG.info("reloading code " + sName);
this.filterRegistry.remove(sName);
}
}
ZuulFilter filter = this.filterRegistry.get(sName);
if (filter == null) {
Class clazz = COMPILER.compile(sCode, sName);
if (!Modifier.isAbstract(clazz.getModifiers())) {
filter = FILTER_FACTORY.newInstance(clazz);
}
}
return filter;
}
public int filterInstanceMapSize() {
return this.filterRegistry.size();
}
public boolean putFilter(File file) throws Exception {
String sName = file.getAbsolutePath() + file.getName();
if (this.filterClassLastModified.get(sName) != null && file.lastModified() != (Long)this.filterClassLastModified.get(sName)) {
LOG.debug("reloading filter " + sName);
this.filterRegistry.remove(sName);
}
ZuulFilter filter = this.filterRegistry.get(sName);
if (filter == null) {
Class clazz = COMPILER.compile(file);
if (!Modifier.isAbstract(clazz.getModifiers())) {
filter = FILTER_FACTORY.newInstance(clazz);
List<ZuulFilter> list = (List)this.hashFiltersByType.get(filter.filterType());
if (list != null) {
this.hashFiltersByType.remove(filter.filterType());
}
this.filterRegistry.put(file.getAbsolutePath() + file.getName(), filter);
this.filterClassLastModified.put(sName, file.lastModified());
return true;
}
}
return false;
}
FilterLoader提供了filter的put,get等方法,具体filter内容是从FilterRegistry获取filters
public class FilterRegistry {
private static final FilterRegistry INSTANCE = new FilterRegistry();
private final ConcurrentHashMap<String, ZuulFilter> filters = new ConcurrentHashMap();
public static final FilterRegistry instance() {
return INSTANCE;
}
private FilterRegistry() {
}
public ZuulFilter remove(String key) {
return (ZuulFilter)this.filters.remove(key);
}
public ZuulFilter get(String key) {
return (ZuulFilter)this.filters.get(key);
}
public void put(String key, ZuulFilter filter) {
this.filters.putIfAbsent(key, filter);
}
public int size() {
return this.filters.size();
}
public Collection<ZuulFilter> getAllFilters() {
return this.filters.values();
}
}
FilterRegistry中是有ConcurrentHashMap<String, ZuulFilter> 存储着所有的filters,
FilterFileManager中提供了定时扫指定文件下的GroovyFileFilter拦截器,进行添加到FilterRegistry中,从而实现了拦截
public class FilterFileManager {
private static final Logger LOG = LoggerFactory.getLogger(FilterFileManager.class);
String[] aDirectories;
int pollingIntervalSeconds;
Thread poller;
boolean bRunning;
static FilenameFilter FILENAME_FILTER;
static FilterFileManager INSTANCE;
private FilterFileManager() {
this.bRunning = true;
}
public static void setFilenameFilter(FilenameFilter filter) {
FILENAME_FILTER = filter;
}
public static void init(int pollingIntervalSeconds, String... directories) throws Exception, IllegalAccessException, InstantiationException {
if (INSTANCE == null) {
INSTANCE = new FilterFileManager();
}
INSTANCE.aDirectories = directories;
INSTANCE.pollingIntervalSeconds = pollingIntervalSeconds;
INSTANCE.manageFiles();
INSTANCE.startPoller();
}
public static FilterFileManager getInstance() {
return INSTANCE;
}
public static void shutdown() {
INSTANCE.stopPoller();
}
void stopPoller() {
this.bRunning = false;
}
void startPoller() {
this.poller = new Thread("GroovyFilterFileManagerPoller") {
public void run() {
while(FilterFileManager.this.bRunning) {
try {
sleep((long)(FilterFileManager.this.pollingIntervalSeconds * 1000));
FilterFileManager.this.manageFiles();
} catch (Exception var2) {
var2.printStackTrace();
}
}
}
};
this.poller.setDaemon(true);
this.poller.start();
}
参考地址:https://blog.csdn.net/forezp/article/details/76211680
FilterFileManager的文章参考:https://www.cnblogs.com/lexiaofei/p/7098136.html
https://blog.csdn.net/haha7289/article/details/54344150
zull上传大文件限制,可通过路径加上zull解决,例如http://ip/zull/api/gate/实现绕过springMVC的DispatcherServlet,详细可参考:https://blog.csdn.net/it_qingfengzhuimeng/article/details/89038122