Zuul自定义过滤器,ZuulProxyAutoConfiguration 如何生效,以及zuul底层源码过滤器初始化流程

自定义ZuulFilter


public class MyZuulFliter extends ZuulFilter {
    
    

    /**
     * 过滤类型
     *
     * @return 过滤类型字符串
     */
    @Override
    public String filterType() {
    
    
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 过滤器的执行顺序
     *
     * @return
     */
    @Override
    public int filterOrder() {
    
    
        return 0;
    }

    /**
     * 是否执行过滤器
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
    
    
        return true;
    }

    /**
     * 过滤器执行逻辑
     *
     * @return 无关紧要
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
    
    
        System.out.println("Entered MyZuulFilter run Method");
        /*
            获取当前请求上下文
            该请求上下文已经被其他过滤器封装好 可以直接拿过来使用
         */
        RequestContext requestContext = RequestContext.getCurrentContext();
        //获取当前请求域对象
        HttpServletRequest request = requestContext.getRequest();

        String method = request.getMethod();
        String URL = request.getRequestURL().toString();

        System.out.println("method: " + method + ",url " + URL);
        return null;
    }
}

执行http://localhost:5000/eureka-client-api/info 打印如下 此时过滤器执行成功
在这里插入图片描述

@EnableZuulProxy

当我们在启动类上加了@EnableZuulProxy 注解时 可以观察其内部结构

/**
 *设置一个Zuul服务器端点并在其中安装一些反向代理过滤器,以便它可以将请求转发到后端服务器。
 后端可以通过配置或DiscoveryClient手动注册。
 *
 * @see EnableZuulServer for how to get a Zuul server without any proxying
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Biju Kunjummen
 */
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class) //导入maker类
public @interface EnableZuulProxy {
    
    

}

在这里插入图片描述

ZuulProxyAutoConfiguration

在这里插入图片描述
在这里插入图片描述

由前面课程可知自动装配有两个条件构成一个是spring.factories第二个是@ConditionalOnxxx。当我们在启动类加上了@EnableZuulProxy 注解此时ZuulProxyAutoConfiguration 就会被自动装配

ZuulProperties

我们在yml中配置的属性都被封装到改类当中

内置过滤器

在这里插入图片描述

error过滤器内置到post 因为最后响应还是到post过滤器当中 由上我们可以看出当zuul在过滤各个阶段执行的过滤器,各个阶段的过滤器的执行顺序是根据filterOrder()来决定的。内置过滤器和我们自己定义的一样
在这里插入图片描述

过滤器执行流程

ZuulServlet

核心zuulservlet初始化器并协调zuullter的执行
在这里插入图片描述

扫描二维码关注公众号,回复: 13293550 查看本文章
/*
 该方法是核心方法 用来在请求初始化zuul网关各个阶段的的Filter
*/
  @Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
    
    
        try {
    
    
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
    
    
            /*
            per前置阶段 此时他会去调用ZuulRunner.preRoute() 方法
            preRoute 会去调用FilterProcessor.route()方法
            */
                preRoute();
            } catch (ZuulException e) {
    
    
                error(e);
                postRoute(); 
                return;
            }
            try {
    
    
                route();
            } catch (ZuulException e) {
    
    
                error(e);
                postRoute();
                return;
            }
            try {
    
    
                postRoute();
            } catch (ZuulException e) {
    
    
                error(e);
                return;
            }

        } catch (Throwable e) {
    
    
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
    
    
        //取消设置threadLocal上下文。在请求结束时完成。
            RequestContext.getCurrentContext().unset();
        }
    }

在这里插入图片描述

FilterProcessor

doc
这是执行过滤器的核心类。

这里面定义了4个用来判断当前filter的FilterType()返回类型的方法

  /**
     * runs "post" filters which are called after "route" filters. ZuulExceptions from ZuulFilters are thrown.
     * Any other Throwables are caught and a ZuulException is thrown out with a 500 status code
     *
     * @throws ZuulException
     */
    public void postRoute() throws ZuulException {
    
    
        try {
    
    
            runFilters("post");
        } catch (ZuulException e) {
    
    
            throw e;
        } catch (Throwable e) {
    
    
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName());
        }
    }

    /**
     * runs all "error" filters. These are called only if an exception occurs. Exceptions from this are swallowed and logged so as not to bubble up.
     */
    public void error() {
    
    
        try {
    
    
            runFilters("error");
        } catch (Throwable e) {
    
    
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * Runs all "route" filters. These filters route calls to an origin.
     *
     * @throws ZuulException if an exception occurs.
     */
    public void route() throws ZuulException {
    
    
        try {
    
    
            runFilters("route");
        } catch (ZuulException e) {
    
    
            throw e;
        } catch (Throwable e) {
    
    
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + e.getClass().getName());
        }
    }

runFilters筛选器

 /**
     * 运行筛选器类型的所有筛选器sType/在筛选器中使用此方法按类型运行自定义筛选器
     *
    	 改方法将相同类型的过滤器链接放到set集合
     * @param sType the filterType.
     * @return
     * @throws Throwable throws up an arbitrary exception
     */
   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 = list.get(i);
                //执行每个过滤器的run方法前提 shouldFilter为true
                //执行成功和失败会有不同操作
                Object result = processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
    
    
                    bResult |= ((Boolean) result);
                }
            }
        }
        return bResult;
    }

当网关在接收到请求此时处于pre前置状态此时会先处理前置过滤器的内容
在这里插入图片描述

此时preRoute() 判断出属于前置方法此时就会去执行前置的所有过滤器
此时pre阶段执行的过滤器有6个
在这里插入图片描述
在这里插入图片描述

processZuulFilter

该方法处理一个单独的ZuulFilter。此方法添加调试信息。通俗来说就是用来执行当前单独的filter然后对其成功失败做对应处理

 public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
    
    

        RequestContext ctx = RequestContext.getCurrentContext();
        boolean bDebug = ctx.debugRouting();
        final String metricPrefix = "zuul.filter-";
        long execTime = 0;
        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();
            }
            /*
            运行过滤器检查!isFilterDisabled()和shouldFilter()。如果两者都为true,
            则调用当前filter 的run()方法。
            */
            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);
                    }
                    break;
                default:
                    break;
            }
            
            if (t != null) throw t;
			//通知线程 当前filter执行完成
            usageNotifier.notify(filter, s);
            return o;

        } catch (Throwable e) {
    
    
            if (bDebug) {
    
    
                Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage());
            }
            usageNotifier.notify(filter, ExecutionStatus.FAILED);
            if (e instanceof ZuulException) {
    
    
                throw (ZuulException) e;
            } else {
    
    
                ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
                ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
                throw ex;
            }
        }
    }

在这里插入图片描述
此时一个filter完整执行完成 FilterProcessor执行当前阶段第二个filter

在这里插入图片描述

各个阶段运行逻辑一致 当Filter初始化,请求完成后
在这里插入图片描述

总结

当客户端在发送请求时Zuul会开辟一个线程池 线程执行时ZuulServlet.server方法拦截到对应请求此时该方法会调用FilterProcessor中的方法依次初始化zuul的pre,rout,post,error各个阶段的过滤器。当过滤器执行过滤完请求后,请求响应 执行结束。

猜你喜欢

转载自blog.csdn.net/qq_42261668/article/details/108299189