Zuul原理
阅读原文时间:2023年07月09日阅读:1

@EnableZuulProxy和@EnableZuulServer通过实例化不同的Marker,走不同的AutoConfiguration。

@EnableZuulProxy所走的ZuulProxyAutoConfiguration基础于@EnableZuulServer的ZuulServerAutoConfiguration。

所以两者的区别在于ZuulProxyAutoConfiguration在ZuulServerAutoConfiguration的基础上增加eureka、ribbon、hystrix等功能。

Zuul配置文件读取是通过在@Configuration注解的类上加@EnableConfigurationProperties注解引入ZuulProperties,而去自动读取的。

ZuulProperties通过routes存放每个路由信息。

ZuulServlet的实例化位于ZuulServerAutoConfiguration中,其通过ServletRegistrationBean注册了监听/zuul路径的ZuulServlet。

    @Bean
    @ConditionalOnMissingBean(name = "zuulServlet")
    @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false", matchIfMissing = true)
    public ServletRegistrationBean zuulServlet() {
        ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(
                new ZuulServlet(), this.zuulProperties.getServletPattern());
        servlet.addInitParameter("buffer-requests", "false");
        return servlet;
    }

但我们使用过程中访问的路径是不带/zuul的,其实现是通过ZuulServerAutoConfiguration实例化的ZuulController和ZuulHandlerMapping来实现的。

ZuulController继承了ServletWrappingController,将请求委托给ZuulServlet处理。

public class ZuulController extends ServletWrappingController {

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

    @Override
    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        try {
            return super.handleRequestInternal(request, response);
        }
        finally {
        RequestContext.getCurrentContext().unset();
        }
    }

}

而ZuulHandlerMapping继承了AbstractUrlHandlerMapping,通过重写lookupHandler,将配置文件所配置的路径交给ZuulController处理。

public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {

    private final RouteLocator routeLocator;

    private final ZuulController zuul;

    private volatile boolean dirty = true;

    @Override
    protected Object lookupHandler(String urlPath, HttpServletRequest request)
            throws Exception {
        RequestContext ctx = RequestContext.getCurrentContext();
        if (ctx.containsKey("forward.to")) {
            return null;
        }
        //刷新新Route配置
        if (this.dirty) {
            synchronized (this) {
                if (this.dirty) {
                    registerHandlers();
                    this.dirty = false;
                }
            }
        }
        return super.lookupHandler(urlPath, request);
    }

    //Route配置的路径,注册处理器为ZuulController
    private void registerHandlers() {
        Collection<Route> routes = this.routeLocator.getRoutes();
        if (routes.isEmpty()) {
            this.logger.warn("No routes found from RouteLocator");
        }
        else {
            for (Route route : routes) {
                registerHandler(route.getFullPath(), this.zuul);
            }
        }
    }

}

于是当我们配置一个route,hello->localhost:8080,我们可以通过/zuul/hello去访问,也可以通过/hello去访问。

下面我们看下ZuulServlet的逻辑,可以发现ZuulServlet的逻辑很简单,都是交给ZuulFilter去实现。

public class ZuulServlet extends HttpServlet {

    private static final long serialVersionUID = -3374242278843351500L;
    private ZuulRunner 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);
    }

    @Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

            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();
        }
    }

}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章