Spring Cloud Zuul执行流程分析
公司(外包)最近在做一个网关的项目,用到了zuul,出于好奇,看了下zuul的代码实现,做个简单记录。
Spring Boot版本是2.0.6.RELEASE,Spring Cloud版本是Finchley.SR1,Spring Cloud是一个版本一个代号,让人头痛。
在maven中引入zuul的依赖后,在启动类上加上@EnableZuulProxy注解就可以实现网关的功能,那么这个注解里面有什么?

可以看到,这里import了另外一个注解ZuulProxyMarkerConfiguration

通过查看这个注解使用的地方,来到ZuulProxyAutoConfiguration
可以看到这里Spring Cloud默认帮我们加载了许多的过滤器



这些过滤器没有全部贴出来,但是这些过滤器可以说就是Zuul的核心代码。同时可以看到,还有ZuulHandlerMapping、ZuulController、ZuulServlet,有没有很熟悉,是的,Zuul就是基于Spring MVC来实现的。
让我们来看一个完整的处理流程
请求会先进入Spring MVC的DispatcherServlet

进入getHandle方法

在handlerMappings中可以看到一个熟悉的ZuulHandlerMapping

直接进入ZuulHandlerMapping的lookupHandler方法

RequestContext.getCurrentContext();这里是帮我们初始化了一个requestContext上下文,在这一个请求的上下游过滤器中都可以使用,接着向下看


这个handlerMap中就是我们在配置文件中配置的路由映射关系,到这里就会跟据我们请求的url匹配出一个最佳的handler,而value就是ZuulController,然后dispatcherServlet会将请求交给ZuulController处理,进入ZuulController

可以看到这里有一个ZuulServlet,是我们之前在ZuulProxyAutoConfiguration中实例化的吗?先看一下ServletWrappingController

这里有一个servletInstance,我们猜肯定是zuulServlet的实例,但是如何实例化的呢?这里ServletWrappingController还实现了接口InitializingBean,看下它的afterPropertiesSet()方法

这里就利用了反射创建一个新的实例

到这里,就会进入ZuulServlet的处理

preRoute()、route()、postRoute()分别是执行pre/route/post三种类型的过滤器,在pre或者route类型抛出ZuulException之后,会进入error()方法,也就是执行error类型的过滤器,再执行postRoute(),如果是postRoute()阶段报错,就只会执行error(),如果抛出的异常不是ZuulException,也是会执行error(),只是zuul把异常重新包装成了ZuulException
preRoute()、route()、postRoute(),这3个方法的逻辑其实是一样的,我们挑一个看


这里会先根据过滤器的类型获取一个filter的list,然后遍历list执行所有的过滤器

执行单个的filter时,会先执行shouldFilter()方法,判断这个过滤器需不需要执行,如果返回true才会执行run方法,其实看到这里,zuul的整个调用栈就已经比较清楚了。
关于zuul是怎么实现的请求的转发,下一篇再讲。
第一次写博客,不得不说,写得真是渣!