SpringCloud-Zuul执行流程分析

前言

如果使用了微服务的springcloud中间件,可能前期早些时候的项目还是在使用zuul,后期的项目基本都要是选择了gateway,但是这里还是想理一理这个zull的流程,毕竟是一个开创网关先补补祖。

知识前提

我们知道,HTTP请求是受HttpServlet接收的,早期项目中其实也是由HttpServlet的doGet doPost方法接收请求进行初始处理并且进行相关逻辑处理。那个年代的项目写到真是想吐。
而zuul是继承了这个HttpServlet,之后便是通过相关的type获取要执行的filter通过遍历方式执行run
另外,我们需了解继承一个类后执行重写的方法是怎么执行的。

接口与继承类相关方法的执行说明

定义接口

public interface ITestFilter {
    void run();
}

定义父类

public abstract  class TestFex implements ITestFilter{
    public void runFilter(){
        run();
    }
}

创建子类与实现类

public class TestFilteImpl extends TestFex {
    @Override
    public void run() {
        System.out.println("=========sub test=======");
    }
}

通过上面的代码,当我们进行以下方法创建与调用时,那么子类的run()就会被执行。

public class TestDemo {
    public static void main(String[] args) {
        TestFilteImpl testFilte = new TestFilteImpl();
        TestFex testFex = new TestFex() {
            @Override
            public void run() {
               
            }
        };
        testFilte.runFilter();
    }
}

留意到上面的代码,testFilte.runFilter();,但是子类没有具体的实现,而执行此方法是回到父类去执行的,而父类又执行了run(),此时的run()是子类的。即体现了父类没有相关的方法时,则执行的是子类的(实现同一接口的方法)
zuul的Filter执行连正是通过这种方式去完成各个过滤器的调用逻辑与处理的。

开篇

Zuul分发源自DispatcherServlet

DispatcherServlet作为请求分发器,Zuul集成了MVC,所以请求与SpringMvc一样,也是受DispatcherServlet分发的

由分发器到ZuulHandlerMapping

负责[初始化ZuulController RouteLocator,根据URL去配置相关的映射,

public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) {
		this.routeLocator = routeLocator;
		this.zuul = zuul;
		setOrder(-200);
	}

控制器ZuulController

负责初始化ZuulServlet

public ZuulController() {
		setServletClass(ZuulServlet.class);
		setServletName("zuul");
		setSupportedMethods((String[]) null); // Allow all
	}

发起请求进入ZuulServlet

从类定义,可以看出继承关系。请求来了就进入到service方法。
总体走向是init --> preRoute() -->route(); -->postRoute();,当然这里不考虑异常情况,下面的分析是以preRoute()来说明,其他的是相似或者可以说就是一样的。

public class ZuulServlet extends HttpServlet

@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 {
                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 {
            RequestContext.getCurrentContext().unset();
        }
    }


创建上下文

RequestContext context = RequestContext.getCurrentContext();
上面是通过threadlocal设置的上下文,所以说一次请求的上下文不会受并发影响就是这个原因。

preRoute()方法执行过程

1、进入此方法即调用zuulRunner的preRoute()

void preRoute() throws ZuulException {
  zuulRunner.preRoute();
}

2、进入zuulRunner的preRoute即是从当前FilterProcessor获取这一前辍的Filter实例与执行

public void preRoute() throws ZuulException {
        FilterProcessor.getInstance().preRoute();
    }

3、根据pre获取这一类型的Filter并执行

public void preRoute() throws ZuulException {
        try {
            runFilters("pre");
        } catch (ZuulException e) {
            throw e;
        } catch (Throwable e) {
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
        }
    }

4、进入runFilters(“pre”);
根据pre获取所有的pre类型的Filter

List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);

5、从中选取为pre的Filter存放到List并返回

public List<ZuulFilter> getFiltersByType(String filterType) {

        List<ZuulFilter> list = hashFiltersByType.get(filterType);
        if (list != null) return list;

        list = new ArrayList<ZuulFilter>();
//从已经注册的Filter中获取pre类型的Filter
        Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
        for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
            ZuulFilter filter = iterator.next();
            if (filter.filterType().equals(filterType)) {
                list.add(filter);
            }
        }
        Collections.sort(list); // sort by priority

        hashFiltersByType.putIfAbsent(filterType, list);
        return list;
    }

6、处理返回的List,留意processZuulFilter
通过遍历,执行相关的Filter,入口是processZuulFilter(zuulFilter);

for (int i = 0; i < list.size(); i++) {
                ZuulFilter zuulFilter = list.get(i);
                Object result = processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
                    bResult |= ((Boolean) result);
                }
            }

7、以下简要讲主要执行的代码

public Object processZuulFilter(ZuulFilter filter) throws ZuulException {

        RequestContext ctx = RequestContext.getCurrentContext();         
            ZuulFilterResult result = filter.runFilter();
    }

8、进入调用子类的shouldFilter(),返回true时则再执行run()

public ZuulFilterResult runFilter() {
        ZuulFilterResult zr = new ZuulFilterResult();
        if (!isFilterDisabled()) {
            if (shouldFilter()) {
                Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
                try {
                    //以下代码就是会执行子类的run了,上面说过,父类没有则执行子类的方法,但是这里是怎么知道是哪个子类的,是在哪里处理不确定是不是上面那行代码。这里再研究。
                    Object res = run();
                    zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                } catch (Throwable e) {
                    t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
                    zr = new ZuulFilterResult(ExecutionStatus.FAILED);
                    zr.setException(e);
                } finally {
                    t.stopAndLog();
                }
            } else {
                zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
            }
        }
        return zr;
    }

9、调用run即进入当前子类的run()

到此,一个zuul的处理流程已经大致说完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值