zoukankan      html  css  js  c++  java
  • zuul源码(2)

    路由

    路由是网关的核心功能,既然在spring的框架下,那就要按Spring的规矩来。

    路由规则类:org.springframework.cloud.netflix.zuul.filters.Route 维护这以下信息:

    private String id;
    
    private String fullPath;
    
    private String path;
    
    private String location;
    
    private String prefix;
    
    private Boolean retryable;
    
    private Set<String> sensitiveHeaders = new LinkedHashSet<>();
    
    private boolean customSensitiveHeaders;
    
    private boolean prefixStripped = true;
    

    路由规则维护:RouteLocator

    public interface RouteLocator {
    
       /**
        * Ignored route paths (or patterns), if any.
        */
       Collection<String> getIgnoredPaths();
    
       /**
        * A map of route path (pattern) to location (e.g. service id or URL).
        */
       List<Route> getRoutes();
    
       /**
        * Maps a path to an actual route with full metadata.
        */
       Route getMatchingRoute(String path);
    
    }
    

    类结构如下:

    自动配置代码:

    @Bean
    @ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
    public DiscoveryClientRouteLocator discoveryRouteLocator() {
       return new DiscoveryClientRouteLocator(this.server.getServletPrefix(),
             this.discovery, this.zuulProperties, this.serviceRouteMapper, this.registration);
    }
    
    @Bean
    @Primary
    public CompositeRouteLocator primaryRouteLocator(
          Collection<RouteLocator> routeLocators) {
       return new CompositeRouteLocator(routeLocators);
    }
    
    @Bean
    @ConditionalOnMissingBean(SimpleRouteLocator.class)
    public SimpleRouteLocator simpleRouteLocator() {
       return new SimpleRouteLocator(this.server.getServletPrefix(),
             this.zuulProperties);
    }
    

    这里会使用DiscoveryClientRouteLocator,它做了一个事就是利用DiscoveryClient把注册中心的信息捞过来直接做映射成为路由规则列表。具体代码写的也有差点意思的,比如下图:

    首先有ZuulController和ZuulHandlerMapping,请求进来先在ZuulHandlerMapping里的列表上找有没有,如果有就把请求丢给ZuulController处理。所以里面一定维护这个一个<path,ZuulController>这么个map。这个匹配规则哪里来呢,一般我们认为是配置,但这里用了spring cloud,它加了注册的微服务动态加入匹配规则的逻辑,也就是下面的DiscoveryClientRouteLocator。
    ZuulHandlerMapping的lookupHandler方法:

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

    这里看到this.dirty来控制是不是重新调用registerHandlers,看代码是执行一遍后,列表被存下来后面进来就用这个列表就行了。这里有一个点,就是每次心跳事件,就是应用和注册中心保持的心跳的时候会把这个重新改成true,从而再执行到locateRoutes方法,就可以重新在内存里拿注册中心可能同步过来的新的信息。

    注意ZuulProxyAutoConfiguration中ZuulDiscoveryRefreshListener的onApplicationEvent方法:

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

    locateRoutes方法就是从注册中心同步过来的所有注册的应用,转成一个个路由规则,如果自己在配置上配置了的路由规则,则按配置的来,没配置的就补上。这里注意了,也就是说不管你配置没配置,只要注册上来的,这个网关的路由规则上就有!问题是,很多微服务提供的接口并不想给网关用,甚至从分层的角度上来说某些应用属于基础服务应用,只想给上层的业务应用调用,并不想直接由网关暴露出去。那么是不是有问题呢,这个也不算是问题,人家提供的框架本来就是要你在这个基础上改造的。这个后续再讲。
    locateRoutes方法:

    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
          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;
    }
    
  • 相关阅读:
    108. Convert Sorted Array to Binary Search Tree
    How to check if one path is a child of another path?
    Why there is two completely different version of Reverse for List and IEnumerable?
    在Jenkins中集成Sonarqube
    如何查看sonarqube的版本 how to check the version of sonarqube
    Queue
    BFS广度优先 vs DFS深度优先 for Binary Tree
    Depth-first search and Breadth-first search 深度优先搜索和广度优先搜索
    102. Binary Tree Level Order Traversal 广度优先遍历
    How do I check if a type is a subtype OR the type of an object?
  • 原文地址:https://www.cnblogs.com/killbug/p/9978491.html
Copyright © 2011-2022 走看看