当前位置:首页 >热点 >SpringCloud网关Zuul底层实现原理详解 至少都在使用Spring Cloud Gateway

SpringCloud网关Zuul底层实现原理详解 至少都在使用Spring Cloud Gateway

2024-06-30 21:57:22 [百科] 来源:避面尹邢网

SpringCloud网关Zuul底层实现原理详解

作者:Springboot实战案例锦集 开发 前端 Zuul实现是层实基于Servlet这种阻塞是IO这种机制是通过创建更多的线程来弥补其不足;而Cloud Gateway则是基于反应式非阻塞式的,使用少量的现原线程来做更多的事。

Zuul现在应用的理详已经非常少了,至少都在使用Spring Cloud Gateway。层实Zuul实现是现原基于Servlet这种阻塞是IO这种机制是通过创建更多的线程来弥补其不足;而Cloud Gateway则是基于反应式非阻塞式的,使用少量的理详线程来做更多的事。以下是层实官方对阻塞与非阻塞的对比图:

图片图片

SpringCloud网关Zuul底层实现原理详解 至少都在使用Spring Cloud Gateway

1 收集路由

public class ZuulServerAutoConfiguration {    @Autowired   protected ZuulProperties zuulProperties;   @Autowired   protected ServerProperties server;   // 主   @Bean   @Primary   public CompositeRouteLocator primaryRouteLocator(       Collection<RouteLocator> routeLocators) {      return new CompositeRouteLocator(routeLocators);   }    @Bean   @ConditionalOnMissingBean(SimpleRouteLocator.class)   public SimpleRouteLocator simpleRouteLocator() {      return new SimpleRouteLocator(this.server.getServlet().getContextPath(), this.zuulProperties);   } }

SimpleRouteLocator

public class SimpleRouteLocator implements RouteLocator, Ordered {    private ZuulProperties properties;   private String dispatcherServletPath = "/";   private String zuulServletPath;   private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>();   // servletPath = server.servlet.contextPath 配置属性值   public SimpleRouteLocator(String servletPath, ZuulProperties properties) {      this.properties = properties;     if (StringUtils.hasText(servletPath)) {        this.dispatcherServletPath = servletPath;     }     // 默认为:/zuul     this.zuulServletPath = properties.getServletPath();   }    // 该方法会在CompositeRouteLocator中调用,而本例的现原作用就是将ZuulRoute转换为Route对象   @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();       try {          // 将配置文件中配置的ZuulRoute路由转换为Route对象         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();   }   protected Map<String, ZuulRoute> locateRoutes() {      LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();     // properties.getRouets获取配置文件中配置的所有路由     for (ZuulRoute route : this.properties.getRoutes().values()) {        routesMap.put(route.getPath(), route);     }     return routesMap;   }   protected Route getRoute(ZuulRoute route, String path) {      String targetPath = path;     // 获取配置文件的zuul.prefix属性值     String prefix = this.properties.getPrefix();     if (prefix.endsWith("/")) {        // 删除 '/' 结尾的字符       prefix = prefix.substring(0, prefix.length() - 1);     }     // 判断配置的路由path属性值的开始字符串是不是以 '/prefix/'开头,如:/api/     // 如果配置的理详path:/api/api-1/**则匹配第一个条件,再判断zuul.strip-prefix属性值是层实否为true     if (path.startsWith(prefix + "/") && this.properties.isStripPrefix()) {        // 成立则会截取字符串删除'/prefix/'开头的字符,则最终targetPath = /api-1/**       targetPath = path.substring(prefix.length());     }     // 如果配置为true     if (route.isStripPrefix()) {        // 知道path中第一个'*'       int index = route.getPath().indexOf("*") - 1;       // 如果存在       if (index > 0) {          // 截取第一个'*'之前的现原字符串         // 如上:最终routePrefix = /api-1         String routePrefix = route.getPath().substring(0, index);         // 结合上面:targetPath = /**         targetPath = targetPath.replaceFirst(routePrefix, "");         // 结合上面:prefix = /api + /api-1 = /api/api-1         prefix = prefix + routePrefix;       }     }     // 上面的路径处理就是将配置的zuul.prefix + zuul.routes.xx.path     Boolean retryable = this.properties.getRetryable();     if (route.getRetryable() != null) {        retryable = route.getRetryable();     }     // 构建Route对象     return new Route(route.getId(), targetPath, route.getLocation(), prefix,         retryable,         route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null,         route.isStripPrefix());   } }

CompositeRouteLocator

public class CompositeRouteLocator implements RefreshableRouteLocator {    private final Collection<? extends RouteLocator> routeLocators;   private ArrayList<RouteLocator> rl;   // 收集所有RouteLocator,如上面的理详SimpleRouteLocator   public CompositeRouteLocator(Collection<? extends RouteLocator> routeLocators) {      rl = new ArrayList<>(routeLocators);     AnnotationAwareOrderComparator.sort(rl);     this.routeLocators = rl;   }   public List<Route> getRoutes() {      List<Route> route = new ArrayList<>();     for (RouteLocator locator : routeLocators) {        route.addAll(locator.getRoutes());     }     return route;   } }

这里的RouteLocator有何用,下面会见到。层实

SpringCloud网关Zuul底层实现原理详解 至少都在使用Spring Cloud Gateway

2 注册HandlerMapping

Zuul会注册自身特定的现原ZuulHandlerMapping对象。

SpringCloud网关Zuul底层实现原理详解 至少都在使用Spring Cloud Gateway

public class ZuulServerAutoConfiguration {    // Controller接口;一个controller接口可以通过实现Controller或AbstractController接口来实现   @Bean   public ZuulController zuulController() {      return new ZuulController();   }    @Bean   public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,理详       ZuulController zuulController) {      // HandlerMapping中注入了RouteLocator,也就是上面的CompositeRouteLocator对象     ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController);     mapping.setErrorController(this.errorController);     mapping.setCorsConfigurations(getCorsConfigurations());     return mapping;   } }

zuulController

public class ZuulController extends ServletWrappingController {     public ZuulController() {      // 调用父类的方法设置父类的成员变量servletClass 该Class是个Servlet     // 在下面会看到如何使用ZuulServlet对象     setServletClass(ZuulServlet.class);     setServletName("zuul");     // 支持任意的请求方法     setSupportedMethods((String[]) null); // Allow all   }    // 该方法是Controller接口的核心方法   @Override   public ModelAndView handleRequest(HttpServletRequest request,       HttpServletResponse response) throws Exception {      try {        return super.handleRequestInternal(request, response);     }   } }

ZuulHandlerMapping

HandlerMapping作用就是从通过请求的URI查找到合适的处理句柄,找到就之间返回,接下来就是由HandlerAdapter进行实际的处理了

public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {    private volatile boolean dirty = true;   private final ZuulController zuul;   public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) {      this.routeLocator = routeLocator;     this.zuul = zuul;     // 优先级就很高了,值越小优先级越高     setOrder(-200);   }   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) {            // 首次时dirty为true,这时候就会注册处理句柄了           registerHandlers();           this.dirty = false;         }       }     }     // 调用父类的方法查找合适的请求处理器,从父类handlerMap属性(Map)集合中查找     return super.lookupHandler(urlPath, request);   }   private void registerHandlers() {      // 这里就调用了上面CompositeRouteLocator#getRoutes,这样就得到了所有的路由信息     Collection<Route> routes = this.routeLocator.getRoutes();     if (routes.isEmpty()) {        this.logger.warn("No routes found from RouteLocator");     } else {        for (Route route : routes) {          // 注意:所有的请求路径都对应同一个Handler也就是上面的ZuulController对象         // 到这就知道了所有的路由请求都会被ZuulController处理         // 调用父类方法,将路由信息添加到父类handlerMap属性中(Map)集合         registerHandler(route.getFullPath(), this.zuul);       }     }   } }

3 路由调用

一个请求过来时,通过上面的ZuulHandlerMapping对象找到了合适的处理句柄也就是ZuulController,接下来就是由合适的HandlerAdapter进行真正的调用了。

现在确定了handler对象是ZuulController,接下来就是查看那个HandlerAdatper能够进行处理了。

ZuulController实现了Controller接口,能够处理该类型的是:SimpleControllerHandlerAdaper。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {    public boolean supports(Object handler) {      return (handler instanceof Controller);   }    @Override   @Nullable   public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)       throws Exception {      // 调用ZuulController#handleRequest方法     return ((Controller) handler).handleRequest(request, response);   } }

回到ZuulController

public class ZuulController extends ServletWrappingController {     public ZuulController() {      // 记住这里我们给父类属性servletClass 设置为ZuulServlet     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 {        // @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter       RequestContext.getCurrentContext().unset();     }   }  } public class ServletWrappingController extends AbstractController     implements BeanNameAware, InitializingBean, DisposableBean {    private Class<? extends Servlet> servletClass;   public void setServletClass(Class<? extends Servlet> servletClass) {      this.servletClass = servletClass;   }   protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)       throws Exception {      // 调用ZuulServlet#service方法(具体的处理还是通过ZuulServlet处理)     this.servletInstance.service(request, response);     return null;   }   // 该Bean在初始化解决会通过serveltClass实例化ZuulServlet对象。   public void afterPropertiesSet() throws Exception {      if (this.servletClass == null) {        throw new IllegalArgumentException("'servletClass' is required");     }     if (this.servletName == null) {        this.servletName = this.beanName;     }     // 实例化     this.servletInstance = ReflectionUtils.accessibleConstructor(this.servletClass).newInstance();     // servlet初始化     this.servletInstance.init(new DelegatingServletConfig());   } }

ZuulServlet

public class ZuulServlet extends HttpServlet {    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);   }   // 任意阶段发生了异常都会执行error,post阶段   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 {          // pre节点的ZuulFilter执行         preRoute();       } catch (ZuulException e) {          // error阶段         error(e);         postRoute();         return;       }       try {          // route阶段的路由执行         // 这里其实就是通过网络api调用目标服务的接口了         // 我们可以实现自己的处理api接口的调用         // 如果你真的自己实现了,那么请记得最后执行这行代码         // context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running         // 这样就阻止默认的执行了         // SimpleHostRoutingFilter 网络请求处理使用的Apache Client         // 在该节点得到数据后我们可以通过ProxyRequestHelper工具类将结果(InputStream)保存到         // RequestContext中,一切顺利在post阶段就会由SendResponseFilter从RequestContext中获取InputStream,然后写到客户端         route();       } catch (ZuulException e) {          error(e);         // 发生了异常也会执行         postRoute();         return;       }       try {          // post阶段         // 比如:系统提供的SendResponseFilter过滤器就是真正往客户端开始写数据了。         postRoute();       } catch (ZuulException e) {          error(e);         return;       }      } catch (Throwable e) {        error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));     } finally {        RequestContext.getCurrentContext().unset();     }   }   void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {      // 将request,response都进行了Wrapper(HttpServletRequestWrapper, HttpServletResponseWrapper)     zuulRunner.init(servletRequest, servletResponse);   } }

在上面就看到了各个阶段的Filter的执行。这里需要注意一点,在每个阶段执行的时候都会通过FilterLoader来获取对应阶段的ZuulFilter。

这个FilterLoader对象并不是一个被容器管理的对象,下面来看这个FilterLoader是如何收集所有ZuulFilter。

public class ZuulServerAutoConfiguration {    @Configuration(proxyBeanMethods = false)   protected static class ZuulFilterConfiguration {       // 获取容器中所有的ZuulFilter     @Autowired     private Map<String, ZuulFilter> filters;      // 该Bean中有通过注解@PostConstruct标注的Bean在初始化阶段执行的方法。     @Bean     public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory,         TracerFactory tracerFactory) {        FilterLoader filterLoader = FilterLoader.getInstance();       FilterRegistry filterRegistry = FilterRegistry.instance();       return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory,           filterLoader, filterRegistry);     }    } } public class ZuulFilterInitializer {    private final Map<String, ZuulFilter> filters;   private final FilterLoader filterLoader;   private final FilterRegistry filterRegistry;   public ZuulFilterInitializer(Map<String, ZuulFilter> filters,       CounterFactory counterFactory, TracerFactory tracerFactory,       FilterLoader filterLoader, FilterRegistry filterRegistry) {      this.filters = filters;     this.counterFactory = counterFactory;     this.tracerFactory = tracerFactory;     this.filterLoader = filterLoader;     this.filterRegistry = filterRegistry;   }    // 该方法作用就是将所有的ZuulFilter添加到FilterRegistry中   @PostConstruct   public void contextInitialized() {      for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {        // 将所有的ZuulFilter注册到FilterRegistry中       filterRegistry.put(entry.getKey(), entry.getValue());     }   } }

FilterLoader

public class FilterLoader {    public Object runFilters(String sType) throws Throwable {      boolean bResult = false;     // 获取对应阶段的ZuulFilter     List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);     if (list != null) {        for (int i = 0; i < list.size(); i++) {          ZuulFilter zuulFilter = list.get(i);         Object result = processZuulFilter(zuulFilter);         if (result != null && result instanceof Boolean) {            bResult |= ((Boolean) result);         }       }     }     return bResult;   }   public List<ZuulFilter> getFiltersByType(String filterType) {      List<ZuulFilter> list = hashFiltersByType.get(filterType);     if (list != null) return list;      list = new ArrayList<ZuulFilter>();      // 从FilterRegistry中获取所有的ZuulFilter;在上面看到了所有的ZuulFilter都被添加到了FilterRegistry中     Collection<ZuulFilter> filters = filterRegistry.getAllFilters();     for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {        ZuulFilter filter = iterator.next();       if (filter.filterType().equals(filterType)) {          list.add(filter);       }     }     Collections.sort(list); // sort by priority      hashFiltersByType.putIfAbsent(filterType, list);     return list;   } }

以上就是Zuul网关的底层实现原理。

责任编辑:武晓燕 来源: 实战案例锦集 ZuulIO反应式

(责任编辑:探索)

    推荐文章
    热点阅读