美文网首页我爱编程
Springcloud zuul 源码阅读

Springcloud zuul 源码阅读

作者: 薛云龙 | 来源:发表于2018-04-13 19:23 被阅读145次

    阅读过SpringCloud基于zuul的实现,大体能够了解其中主要逻辑,但是直白的来讲不知如何下笔。我们通过问题的方式,一步一步的剖析其中的细节。

    看这块内容前,你要对Spring MVC中的DispatchServlet,HandlerMapping及其相关子类有所了解。简单说一下:
    DispatchServlet通过handlerMapping的实现类,把所有controller加载为handler,这样每次过来请求时,根据url寻找对应controller(handler),以此响应用户。
    那么下面就是一个小高潮,


    ZuulHandlerMapping.java

    先暂且不管Route。ZuulHandlerMapping和ZuulController是SpringCloudZuul实现了的两个类。这里的ZuulHandlerMapping将ZuulController与所有的route.getFullPath()对应起来,意思就是所有能访问到的路径,都会先走zuulController这个handler。

    那么我们接着来看ZuulController的实现,


    image.png
    image.png

    ZuulController继承了ServletWrappingController,由ServletWrappingController可知,所有的请求都会走到servletInstance的service方法。那么这个servletInstance就是com.netflix.zuul.http.ZuulServlet。看过我上篇文章NetFlix Zuul 源码阅读

    1.至此,你应该能够理解为什么所有的请求都能被Filter处理?

    那么,我们接着看,你还记不记得上文中的Route类了?上文中有一个操作是

    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.fullpath注册到了zuulController。这里边的Route其实就是一个路由信息的类,你看他的字段就可以得知他的作用了。
    比他更关键的是SimpleRouteLocator和DiscoveryClientRouteLocator,SimpleRouteLocator能够将你配置文件中配置的所有路由信息转为Route对象的一个列表,这样你就能知道配置文件声明的所有路由表啦。那么如果你接入了注册中心,顾名思义,DiscoveryClientRouteLocator则是能够通过DiscoveryClient去注册中心获取到所有能访问的api路径。
    so,通俗点来说,Route就是访问的一个接口路径+location地址的一个信息,RouteLocator更像是统一加载系统中的所有的接口路径信息的匹配器。

    好,我们接下来就是另外一个小高潮。游泳健身(pre---filter)了解一下:


    image.png

    这里主要看PreDecorationFilter这个过滤器:

    public Object run() {
            RequestContext ctx = RequestContext.getCurrentContext();
            final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
            //看见了没?这里有没有根据当前请求的requestURI来找到对应的路由信息
            Route route = this.routeLocator.getMatchingRoute(requestURI);
            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.properties.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 null;
                    }
                    else {
                        // set serviceId for use in filters.route.RibbonRequest
                        ctx.set(SERVICE_ID_KEY, location);
                        ctx.setRouteHost(null);
                        ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
                    }
                    if (this.properties.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.properties.isAddHostHeader()) {
                        ctx.addZuulRequestHeader(HttpHeaders.HOST, toHostHeader(ctx.getRequest()));
                    }
                }
            }
    

    PreDecorationFilter主要是在请求来到时,将需要准备的信息放到线程内RequestContext。然后为下一步的route类型的filter准备数据。

    image.png

    看一下SimpleHostRoutingFilter的实现:

    image.png

    这说明这个filter生效的原因是:线程存储中有routehost这个属性并且在sendZuulResponse()为true。sendZuulResponse这个家伙主要在pre类型的filter中使用,如果sendZuulResponse未false,说明不用继续通过其他的filter直接返回结果。


    image.png

    这里很明显的观察到,CloseableHttpClient对request进行了转发请求,并将CloseableHttpResponse返回。

    再看一下RibbonRoutingFilter

    image.png

    只要有注册中心服务中的serviceId,routehost==null,sendZuulResponse()为true,该filter会生效。

    2. SpringCloudZuul如何实现的路径匹配和转发?

    相关文章

      网友评论

        本文标题:Springcloud zuul 源码阅读

        本文链接:https://www.haomeiwen.com/subject/tqickftx.html