spring-cloud 源码 zuul 路由定位(三)

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

1 RouteLocator 路由器

2019102020051\_1.png
从上面图可以知道,RouteLocator 有三个实现类,在使用@EnableZuulServer 使用到
SimpleRouteLocator,CompositeRouteLocator 作为简单的路由定位,如开启的是@EnableZuulProxy 则使用到DiscoveryClientRouteLocator 及CompositeRouteLocator(该路由器只是组合)

2 路由器如作初始化

结合zuul的第一编源码分析
@EnableZuulServer 生效的配置 ZuulServerAutoConfiguration ,非代理方式转发请求

        @Bean
        @Primary
        public CompositeRouteLocator primaryRouteLocator(
                Collection<RouteLocator> routeLocators) {
            return new CompositeRouteLocator(routeLocators);
        }

        //当SimpleRouteLocator 实例不存在时创建其实例
        @Bean
        @ConditionalOnMissingBean(SimpleRouteLocator.class)
        public SimpleRouteLocator simpleRouteLocator() {
            return new SimpleRouteLocator(this.server.getServletPrefix(),
                    this.zuulProperties);
        }
        //当前创建的RouteLocator 被装入ZuulHandlerMapping(spring-mvc的内容)
        @Bean
        public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
            ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
            mapping.setErrorController(this.errorController);
            return mapping;
        }

@EnableZuulProxy生效的配置 ZuulProxyAutoConfiguration,动态代理路由转发请求
当DiscoveryClientRouteLocator实例不存在时创建其实例,因为DiscoveryClientRouteLocator是SimpleRouteLocator的子类,所有当该实例创建后,SimpleRouteLocator 不会再被创建,而简接装入ZuulHandlerMapping 是DiscoveryClientRouteLocator

        @Bean
        @ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
        public DiscoveryClientRouteLocator discoveryRouteLocator() {
            return new DiscoveryClientRouteLocator(this.server.getServletPrefix(), this.discovery, this.zuulProperties,
                    this.serviceRouteMapper);
        }

3 RooteLocator执行过程

1.从Dispatcher->ZuulHandlerMapping->ZuulController->ZuulServlet->ZuulRunner->FilterProcessor->ZuulFilter->PreDecorationFilter/CompositeRouteLocator(SimpleRouteLocator)
前面三个类调用过程是spring-mvc的内容不再分析,这里重点看ZuulServerlet后面的源码
2.静态路由将会把url及handler controler注册到ZuulHandlerMapping
ZuulHandlerMapping
动态更新路由信息

监听服务信息变化,以更可以动态变更路由信息

    private static class ZuulDiscoveryRefreshListener
                implements ApplicationListener<ApplicationEvent> {

            private HeartbeatMonitor monitor = new HeartbeatMonitor();

            @Autowired
            private ZuulHandlerMapping zuulHandlerMapping;

            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                if (event instanceof InstanceRegisteredEvent) {
                    reset();
                }
                else if (event instanceof ParentHeartbeatEvent) {
                    ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
                    resetIfNeeded(e.getValue());
                }
                else if (event instanceof HeartbeatEvent) {
                    HeartbeatEvent e = (HeartbeatEvent) event;
                    resetIfNeeded(e.getValue());
                }

            }

            private void resetIfNeeded(Object value) {
                if (this.monitor.update(value)) {
                    reset();
                }
            }

            private void reset() {
                this.zuulHandlerMapping.setDirty(true);
            }

        }

标记由路信息是否脏了,如果脏了,则刷新路由信息
2019102020051\_2.png

        @Override
        protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
            if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
                return null;
            }
            if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) return null;
            RequestContext ctx = RequestContext.getCurrentContext();
            if (ctx.containsKey("forward.to")) {
                return null;
            }
            if (this.dirty) {
                synchronized (this) {
                    //如果路由信息脏了,则重新注册新的处理器
                    if (this.dirty) {
                        registerHandlers();
                        this.dirty = false;
                    }
                }
            }
            return super.lookupHandler(urlPath, request);
        }
        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);
                }
            }
        }
        protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
            Assert.notNull(urlPath, "URL path must not be null");
            Assert.notNull(handler, "Handler object must not be null");
            Object resolvedHandler = handler;

            // Eagerly resolve handler if referencing singleton via name.
            if (!this.lazyInitHandlers && handler instanceof String) {
                String handlerName = (String) handler;
                if (getApplicationContext().isSingleton(handlerName)) {
                    resolvedHandler = getApplicationContext().getBean(handlerName);
                }
            }
            //静态路由url处理器是否为传入的(ZuulController)
            Object mappedHandler = this.handlerMap.get(urlPath);
            if (mappedHandler != null) {
                if (mappedHandler != resolvedHandler) {
                    throw new IllegalStateException(
                            "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                            "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
                }
            }
            else {
                //配置根路径
                if (urlPath.equals("/")) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Root mapping to " + getHandlerDescription(handler));
                    }
                    setRootHandler(resolvedHandler);
                }
                //匹配*通配符
                else if (urlPath.equals("/*")) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Default mapping to " + getHandlerDescription(handler));
                    }
                    setDefaultHandler(resolvedHandler);
                }
                else {
                    //保存其它的url对应的handler
                    this.handlerMap.put(urlPath, resolvedHandler);
                    if (logger.isInfoEnabled()) {
                        logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
                    }
                }
            }
        }

4 SimpleRouteLocator

该类主要是处理静态路由

开始获取静态路由信息

        @Override
        public List<Route> getRoutes() {
            List<Route> values = new ArrayList<>();
            for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) {
                ZuulRoute route = entry.getValue();
                String path = route.getPath();
                values.add(getRoute(route, path));
            }
            return values;
        }
        protected Map<String, ZuulRoute> getRoutesMap() {
            if (this.routes.get() == null) {
                this.routes.set(locateRoutes());
            }
            return this.routes.get();
        }

获取静态配置路由信息

        /** * Compute a map of path pattern to route. The default is just a static map from the * {@link ZuulProperties}, but subclasses can add dynamic calculations. */
        protected Map<String, ZuulRoute> locateRoutes() {
            LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
            for (ZuulRoute route : this.properties.getRoutes().values()) {
                routesMap.put(route.getPath(), route);
            }
            return routesMap;
        }

5 DiscoveryClientRouteLocator

继承 SimpleRouteLocator 主要是通过eureka client发现服务信息,以便动态更新路由信息

        @Override
        protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
            LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
            //调用父类方法,获取静态路由信息
            routesMap.putAll(super.locateRoutes());
            if (this.discovery != null) {

                Map<String, ZuulRoute> staticServices = new LinkedHashMap<String, ZuulRoute>();
                for (ZuulRoute route : routesMap.values()) {
                    String serviceId = route.getServiceId();
                    if (serviceId == null) {
                        serviceId = route.getId();
                    }
                    if (serviceId != null) {
                        staticServices.put(serviceId, route);
                    }
                }
                // Add routes for discovery services by default
                //通过discovery发现服务,并动态创建服务路由信息
                List<String> services = this.discovery.getServices();
                String[] ignored = this.properties.getIgnoredServices()
                        .toArray(new String[0]);
                for (String serviceId : services) {
                    // Ignore specifically ignored services and those that were manually
                    // configured
                    String key = "/" + mapRouteToService(serviceId) + "/**";
                    if (staticServices.containsKey(serviceId)
                            && staticServices.get(serviceId).getUrl() == null) {
                        // Explicitly configured with no URL, cannot be ignored
                        // all static routes are already in routesMap
                        // Update location using serviceId if location is null
                        ZuulRoute staticRoute = staticServices.get(serviceId);
                        if (!StringUtils.hasText(staticRoute.getLocation())) {
                            staticRoute.setLocation(serviceId);
                        }
                    }
                    if (!PatternMatchUtils.simpleMatch(ignored, serviceId)
                            && !routesMap.containsKey(key)) {
                        // Not ignored
                        routesMap.put(key, new ZuulRoute(key, serviceId));
                    }
                }
            }
            if (routesMap.get(DEFAULT_ROUTE) != null) {
                ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE);
                // Move the defaultServiceId to the end
                routesMap.remove(DEFAULT_ROUTE);
                routesMap.put(DEFAULT_ROUTE, defaultRoute);
            }
            LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
            for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
                String path = entry.getKey();
                // Prepend with slash if not already present.
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
                if (StringUtils.hasText(this.properties.getPrefix())) {
                    path = this.properties.getPrefix() + path;
                    if (!path.startsWith("/")) {
                        path = "/" + path;
                    }
                }
                values.put(path, entry.getValue());
            }
            return values;
        }
赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring-cloud 源码 zuul 路由定位(三)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏