Zuul 就是个网关,过滤所有数据, 和Eureka的区别就是,前者或过滤数据,一般进行权限拦截,后者进行请求的转发,只是链接。

Zuul包含了对请求的路由和过滤两个最主要的功能:

使用 注解@EnableZuulProxy  引入 ZuulProxyMarkerConfiguration.class

此时导入的配置类也会注入

@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {

  这个配置类中主要注入了已写filter和controller之类的,具体看源码

    @Bean
@ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),
this.zuulProperties.getServletPattern());
// The whole point of exposing this servlet is to provide a route that doesn't
// buffer requests.
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}
初始化ZuulFilterInitializer类,将所有的filter 向FilterRegistry注册。
@Configuration
protected static class ZuulFilterConfiguration { @Autowired
private Map<String, ZuulFilter> filters; @Bean
public ZuulFilterInitializer zuulFilterInitializer(
CounterFactory counterFactory, TracerFactory tracerFactory) {
FilterLoader filterLoader = FilterLoader.getInstance();
FilterRegistry filterRegistry = FilterRegistry.instance();
         //FilterRegistry管理了一个ConcurrentHashMap,用作存储过滤器的
return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
} }

在zuul中, 整个请求的过程是这样的,首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的zuulfilter共享。zuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的zuulfilter的管理器。FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。有了这些filter之后,zuulservelet首先执行的Pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。

ZuulServlet初始化zuulRunner

    @Override
public void init(ServletConfig config) throws ServletException {
super.init(config); String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false; zuulRunner = new ZuulRunner(bufferReqs);
}

zuulRunner初始化RequestContext

    public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        RequestContext ctx = RequestContext.getCurrentContext();
if (bufferRequests) {
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
} ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}

FilterProcessor过滤器处理器:

ZuulFilter主要分类有四种:

PRE: 该类型的filters在Request routing到源web-service之前执行。用来实现Authentication、选择源服务地址等

ROUTING:该类型的filters用于把Request routing到源web-service,源web-service是实现业务逻辑的服务。这里使用HttpClient请求web-service。

POST:该类型的filters在ROUTING返回Response后执行。用来实现对Response结果进行修改,收集统计数据以及把Response传输会客户端。

ERROR:上面三个过程中任何一个出现错误都交由ERROR类型的filters进行处理。

{
pre=[
org.springframework.cloud.netflix.zuul.filters.pre.ServletDetectionFilter@665cb192,
org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter@a3739e1,
org.springframework.cloud.netflix.zuul.filters.pre.FormBodyWrapperFilter@69676b9c,
org.springframework.cloud.netflix.zuul.filters.pre.DebugFilter@666e1965,
com.qinsilk.cloud.gateway.filter.AccessFilter@7da324d0,
com.qinsilk.cloud.gateway.filter.OAuthFilter@2d86f17b,
com.qinsilk.cloud.gateway.filter.DocumentFilter@70a416c,
org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter@4a105d1b,
com.自定义.filter.StaticResponseFilter@4047ca4d
],
route=[
org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter@6bf6b454,
org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter@72a14347,
org.springframework.cloud.netflix.zuul.filters.route.SendForwardFilter@69f4143
],
post=[
org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter@717616da
]
} 
过滤器 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

自定义的filter实现ZuulFilter 或者想在哪个filter前可配置

public abstract class PreDescorationBaseFilter extends ZuulFilter{

    @Autowired
protected ZuulProperties zuulProperties; @Resource(name = "primaryRouteLocator")
private RouteLocator routeLocator; @Resource
private PreDecorationFilter preDecorationFilter; private ProxyRequestHelper proxyRequestHelper = new ProxyRequestHelper(); protected void router(RequestContext ctx, String newUri) {
Route route = this.routeLocator.getMatchingRoute(newUri);
if (route != null) {
String location = route.getLocation();
if (location != null) {
ctx.put(REQUEST_URI_KEY, route.getPath());
ctx.put(PROXY_KEY, route.getId());
if (!route.isCustomSensitiveHeaders()) {
this.proxyRequestHelper.addIgnoredHeaders(this.zuulProperties.getSensitiveHeaders().toArray(new String[0]));
}
else {
this.proxyRequestHelper.addIgnoredHeaders(route.getSensitiveHeaders().toArray(new String[0]));
} if (route.getRetryable() != null) {
ctx.put(RETRYABLE_KEY, route.getRetryable());
} if (location.startsWith(HTTP_SCHEME+":") || location.startsWith(HTTPS_SCHEME+":")) {
ctx.setRouteHost(getUrl(location));
ctx.addOriginResponseHeader(SERVICE_HEADER, location);
}
else if (location.startsWith(FORWARD_LOCATION_PREFIX)) {
ctx.set(FORWARD_TO_KEY,StringUtils.cleanPath(location.substring(FORWARD_LOCATION_PREFIX.length()) + route.getPath()));
ctx.setRouteHost(null);
return ;
}
else {
ctx.set(SERVICE_ID_KEY, location);
ctx.setRouteHost(null);
ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
} if (this.zuulProperties.isAddProxyHeaders()) {
addProxyHeaders(ctx, route);
String xforwardedfor = ctx.getRequest().getHeader(X_FORWARDED_FOR_HEADER);
String remoteAddr = ctx.getRequest().getRemoteAddr();
if (xforwardedfor == null) {
xforwardedfor = remoteAddr;
}
else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates
xforwardedfor += ", " + remoteAddr;
}
ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor);
}
if (this.zuulProperties.isAddHostHeader()) {
ctx.addZuulRequestHeader(HttpHeaders.HOST, toHostHeader(ctx.getRequest()));
}
}
}
} protected URL getUrl(String target) {
try {
return new URL(target);
}
catch (MalformedURLException ex) {
throw new IllegalStateException("Target URL is malformed", ex);
}
} protected void addProxyHeaders(RequestContext ctx, Route route) {
HttpServletRequest request = ctx.getRequest();
String host = toHostHeader(request);
String port = String.valueOf(request.getServerPort());
String proto = request.getScheme();
if (hasHeader(request, X_FORWARDED_HOST_HEADER)) {
host = request.getHeader(X_FORWARDED_HOST_HEADER) + "," + host;
}
if (!hasHeader(request, X_FORWARDED_PORT_HEADER)) {
if (hasHeader(request, X_FORWARDED_PROTO_HEADER)) {
StringBuilder builder = new StringBuilder();
for (String previous : StringUtils.commaDelimitedListToStringArray(request.getHeader(X_FORWARDED_PROTO_HEADER))) {
if (builder.length()>0) {
builder.append(",");
}
builder.append(HTTPS_SCHEME.equals(previous) ? HTTPS_PORT : HTTP_PORT);
}
builder.append(",").append(port);
port = builder.toString();
}
} else {
port = request.getHeader(X_FORWARDED_PORT_HEADER) + "," + port;
}
if (hasHeader(request, X_FORWARDED_PROTO_HEADER)) {
proto = request.getHeader(X_FORWARDED_PROTO_HEADER) + "," + proto;
}
ctx.addZuulRequestHeader(X_FORWARDED_HOST_HEADER, host);
ctx.addZuulRequestHeader(X_FORWARDED_PORT_HEADER, port);
ctx.addZuulRequestHeader(X_FORWARDED_PROTO_HEADER, proto);
addProxyPrefix(ctx, route);
} protected void addProxyPrefix(RequestContext ctx, Route route) {
String forwardedPrefix = ctx.getRequest().getHeader(X_FORWARDED_PREFIX_HEADER);
String contextPath = ctx.getRequest().getContextPath();
String prefix = StringUtils.hasLength(forwardedPrefix) ? forwardedPrefix
: (StringUtils.hasLength(contextPath) ? contextPath : null);
if (StringUtils.hasText(route.getPrefix())) {
StringBuilder newPrefixBuilder = new StringBuilder();
if (prefix != null) {
if (prefix.endsWith("/") && route.getPrefix().startsWith("/")) {
newPrefixBuilder.append(prefix, 0, prefix.length() - 1);
}
else {
newPrefixBuilder.append(prefix);
}
}
newPrefixBuilder.append(route.getPrefix());
prefix = newPrefixBuilder.toString();
}
if (prefix != null) {
ctx.addZuulRequestHeader(X_FORWARDED_PREFIX_HEADER, prefix);
}
} protected boolean hasHeader(HttpServletRequest request, String name) {
return StringUtils.hasLength(request.getHeader(name));
} protected String toHostHeader(HttpServletRequest request) {
int port = request.getServerPort();
if ((port == HTTP_PORT && HTTP_SCHEME.equals(request.getScheme()))
|| (port == HTTPS_PORT && HTTPS_SCHEME.equals(request.getScheme()))) {
return request.getServerName();
}
else {
return request.getServerName() + ":" + port;
}
}
}

请求主要是在routefilter中过滤执行到 SimpleHostRoutingFilter 

在zuul上做日志处理

记录请求的 url,ip地址,参数,请求发生的时间,整个请求的耗时,请求的响应状态,甚至请求响应的结果等,需要写一个ZuulFliter,它应该是在请求发送给客户端之前做处理,并且在route过滤器路由之后.

记录开始时间filter

@Component
public class AccessFilter extends ZuulFilter { @Override
public String filterType() {
return "pre";
} @Override
public int filterOrder() {
return 0;
} @Override
public boolean shouldFilter() {
return true;
} @Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.set("startTime",System.currentTimeMillis()); return null;
}
}

prefilter

结束logfilter

@Component
public class LoggerFilter extends ZuulFilter { @Override
public String filterType() {
return FilterConstants.POST_TYPE;
} @Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
} @Override
public boolean shouldFilter() {
return true;
} @Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String method = request.getMethod();//氢气的类型,post get ..
Map<String, String> params = HttpUtils.getParams(request);
String paramsStr = params.toString();//请求的参数
long statrtTime = (long) context.get("startTime");//请求的开始时间
Throwable throwable = context.getThrowable();//请求的异常,如果有的话
request.getRequestURI();//请求的uri
HttpUtils.getIpAddress(request);//请求的iP地址
context.getResponseStatusCode();//请求的状态
long duration=System.currentTimeMillis() - statrtTime);//请求耗时 return null;
} }

Zuul 源码的分析的更多相关文章

  1. Spring Cloud Zuul源码

    一.Zuul源码分析(初始化流程.请求处理流程)

  2. MapReduce的ReduceTask任务的运行源码级分析

    MapReduce的MapTask任务的运行源码级分析 这篇文章好不容易恢复了...谢天谢地...这篇文章讲了MapTask的执行流程.咱们这一节讲解ReduceTask的执行流程.ReduceTas ...

  3. Activity源码简要分析总结

    Activity源码简要分析总结 摘自参考书籍,只列一下结论: 1. Activity的顶层View是DecorView,而我们在onCreate()方法中通过setContentView()设置的V ...

  4. MapReduce的MapTask任务的运行源码级分析

    TaskTracker任务初始化及启动task源码级分析 这篇文章中分析了任务的启动,每个task都会使用一个进程占用一个JVM来执行,org.apache.hadoop.mapred.Child方法 ...

  5. TaskTracker任务初始化及启动task源码级分析

    在监听器初始化Job.JobTracker相应TaskTracker心跳.调度器分配task源码级分析中我们分析的Tasktracker发送心跳的机制,这一节我们分析TaskTracker接受JobT ...

  6. MongoDB源码分析——mongod程序源码入口分析

    Edit 说明:第一次写笔记,之前都是看别人写的,觉得很简单,开始写了之后才发现真的很难,不知道该怎么分析,这篇文章也参考了很多前辈对MongoDB源码的分析,也有一些自己的理解,后续将会继续分析其他 ...

  7. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  8. FFmpeg源码简单分析:libswscale的sws_scale()

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  9. LinkedHashMap 源码详细分析(JDK1.8)

    1. 概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,Linke ...

随机推荐

  1. (转)代码审计利器-RIPS实践

    一.代码审计工具介绍 代码审计工具可以辅助我们进行白盒测试,大大提高漏洞分析和代码挖掘的效率. 在源代码的静态安全审计中,使用自动化工具辅助人工漏洞挖掘,一款好的代码审计软件,可以显著提高审计工作的效 ...

  2. zabbix (三) 所需磁盘容量计算

    一.zabbix 磁盘容量大小 zabbix的数据库大小取决于NVPS(new values per second)大小,NVPS从总体上反应了处理速度,与监控项的数目,监控项的类型,取值间隔,His ...

  3. Centos7 node npm升级版本

    安装npm 官网:https://nodejs.org/en/download/ 下载LTS Linux Binaries (x64)版本. # tar xf node-v10.16.3-linux- ...

  4. VSCode前端文件(html文件)如何以服务器模式打开?

    方法1: VSCode前端文件(html文件)如何以服务器模式打开?比如工程下有一个A.html文件,想在VSCode里面直接操作,就想Webstorm一样,以http://localhost/xxx ...

  5. 走进JavaWeb技术世界7:Tomcat中的设计模式

    . 门面设计模式 门面设计模式在 Tomcat 中有多处使用,在 Request 和 Response 对象封装中.Standard Wrapper 到 ServletConfig 封装中.Appli ...

  6. Leetcode题 257. Binary Tree Paths

    Given a binary tree, return all root-to-leaf paths. For example, given the following binary tree: 1 ...

  7. idea使用maven私服

    nexus3中央仓库改为阿里云/ 参考:这里写链接内容  找到中央仓库 然后修改成:  http://maven.aliyun.com/nexus/content/groups/public/   1 ...

  8. 利用IDEA上传文件到coding仓库 使用git上传文件github

    1.注册帐号 coding官网: https://coding.net/ github官网:https://github.com (以上根据自己需求注册,在下就不帖图了写步骤了) 2.下载 Git g ...

  9. python根据数组数据绘图

    转载自网络,版权归原作者所有 hello3.txt文件内部数据如下 ......7,2,6,-12,-10,-7,-1,2,9,...... python脚本 import numpy as np i ...

  10. Java点滴-List<Integer> list; 中尖括号的意思

    这是jdk1.5后版本才有的新特性,泛型,指定传入的类型.这样定义之后,这个list只能接收Integer的对象. 以前没有加这个,传入的都是Object类型的,取出来的时候要强制类型转换为自己想要的 ...