2. shenyu(神禹)网关调用链及组装过程

1. 综述

        神禹网关是通过责任链方式组织的,那它的调用链是什么样子的,调用链是怎样组装在一起的,本篇文章就来探索这个问题。

2. 结论先行 

        神禹网关调用链如下所示

        HttpWebHandlerAdapter -> ExceptionHandlingWebHandler -> FilteringWebHandler -> DefaultWebFilterChain ->ShenyuWebHandler

3. 阅读前准备知识

3.1 shenyu网关注册了自己的Handler

        Shenyu网关是基于WebFlux的,在ShenyuConfiguration.java类中,神禹网关注册了自己的WebHandler,ShenyuWebHandler,这里是webflux设计的精巧之处,除了神禹,spring-cloud-gateway也是基于此进行扩展的,spring-cloud-gateway中注册的handler叫DispatcherHandler,此处等我分享spring-cloud-gateway原理的时候在详说,继续看神禹,神禹注册webHandler的代码如下所示。

@Bean("webHandler")
public ShenyuWebHandler shenyuWebHandler(final ObjectProvider> plugins, final ShenyuConfig config) {
    List pluginList = plugins.getIfAvailable(Collections::emptyList);
    List shenyuPlugins = pluginList.stream()
            .sorted(Comparator.comparingInt(ShenyuPlugin::getOrder)).collect(Collectors.toList());
    shenyuPlugins.forEach(shenyuPlugin -> LOG.info("load plugin:[{}] [{}]", shenyuPlugin.named(), shenyuPlugin.getClass().getName()));
    return new ShenyuWebHandler(shenyuPlugins, config);
}

4. HttpHandler的生成过程

4.1 HttpHandler Bean注册进spring

        HttpHandlerAutoConfiguration中类,把HttpHandler注册成了一个bean,HttpHandler生成方式如代码第一行所示,applicatinContext的生成方式参见扩展问题。

@Bean
public HttpHandler httpHandler(ObjectProvider propsProvider) {
   HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build(); //HttpHandler生成方式
   WebFluxProperties properties = propsProvider.getIfAvailable();
   if (properties != null && StringUtils.hasText(properties.getBasePath())) {
      Map handlersMap = Collections.singletonMap(properties.getBasePath(), httpHandler);
      return new ContextPathCompositeHandler(handlersMap);
   }
   return httpHandler;
}

4.2 WebHttpHandlerBuilder.applicationContext(applicationContext)

        applicationContext(applicationContext)代码如下所示,第一行代码,从context中获取WebHandler类的实例,也就是阅读前准备知识中神禹网关注册的ShenyuWebHandler。

        之后它又做了三件事情

  1. 找到所有的webFilters;
  2. 找到所有的exceptionHandlers;
  3. 找到所有的HttpHandlerDecoratorFactory,调用httpHandlerDecorator方法。
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
   //此行代码设置了WebHttpHandlerBuilder中的WebHandler为ShenyuWebHandler
   WebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(
         context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class), context);

   List webFilters = context
         .getBeanProvider(WebFilter.class)
         .orderedStream()
         .collect(Collectors.toList());
   builder.filters(filters -> filters.addAll(webFilters));

   List exceptionHandlers = context
         .getBeanProvider(WebExceptionHandler.class)
         .orderedStream()
         .collect(Collectors.toList());
   builder.exceptionHandlers(handlers -> handlers.addAll(exceptionHandlers));

   context.getBeanProvider(HttpHandlerDecoratorFactory.class)
         .orderedStream()
         .forEach(builder::httpHandlerDecorator);
.....//此处省略一些代码
   return builder;
}

4.3 WebHttpHandlerBuilder.build()方法

        build() 方法最重要的是前三行代码,它生成了一个handler链。

        第一行,使用FilteringWebHandler 包装this.webHandler 即 ShenyuWebHandler,生成如下结构的调用链

        FilteringWebHandler -> ShenyuWebHandler;

        第二行,使用ExceptionHandlingWebHandler 包装第一行的结果,生成如下结构的调用链

        ExceptionHandlingWebHandler -> FilteringWebHadler -> ShenyuWebHandler

        第三行,使用HttpWebHandlerAdapter 包装第二行的结果,生成如下结构的调用链

        HttpWebHandlerAdapter -> ExceptionHandlingWebHandler -> FilteringWebHandler -> ShenyuWebHandler

public HttpHandler build() {
   //第一行
   WebHandler decorated = new FilteringWebHandler(this.webHandler, this.filters);
   //第二行
   decorated = new ExceptionHandlingWebHandler(decorated,  this.exceptionHandlers);
   //第三行
   HttpWebHandlerAdapter adapted = new HttpWebHandlerAdapter(decorated);
   if (this.sessionManager != null) {
      adapted.setSessionManager(this.sessionManager);
   }
   if (this.codecConfigurer != null) {
      adapted.setCodecConfigurer(this.codecConfigurer);
   }
   if (this.localeContextResolver != null) {
      adapted.setLocaleContextResolver(this.localeContextResolver);
   }
   if (this.forwardedHeaderTransformer != null) {
      adapted.setForwardedHeaderTransformer(this.forwardedHeaderTransformer);
   }
   if (this.applicationContext != null) {
      adapted.setApplicationContext(this.applicationContext);
   }
   adapted.afterPropertiesSet();

   return (this.httpHandlerDecorator != null ? this.httpHandlerDecorator.apply(adapted) : adapted);
}

4.4 FilteringWebHandler 内部包装DefaultWebFilterChain

        FilteringWebHandler 类构造函数内部调用了 DefaultWebFilterChain,此时调用链为HttpWebHandlerAdapter -> ExceptionHandlingWebHandler -> FilteringWebHandler -> DefaultWebFilterChain ->ShenyuWebHandler

public FilteringWebHandler(WebHandler handler, List filters) {
   super(handler);
   this.chain = new DefaultWebFilterChain(handler, filters);
}

4.5 DefaultWebFilterChain filter链的构造

DefaultWebFilterChain构造函数如下所示。

public DefaultWebFilterChain(WebHandler handler, List filters) {
   Assert.notNull(handler, "WebHandler is required");
   this.allFilters = Collections.unmodifiableList(filters);
   this.handler = handler;
   DefaultWebFilterChain chain = initChain(filters, handler);
   this.currentFilter = chain.currentFilter;
   this.chain = chain.chain;
}

initChain(filters, handler)方法源码如下所示,通过循环所有的filter,构造成filter链循环调用会生成如下结构的filter chain

2. shenyu(神禹)网关调用链及组装过程_第1张图片

        initChain的源代码如下所示

private static DefaultWebFilterChain initChain(List filters, WebHandler handler) {
   DefaultWebFilterChain chain = new DefaultWebFilterChain(filters, handler, null, null);
   ListIterator iterator = filters.listIterator(filters.size());
   while (iterator.hasPrevious()) {
      chain = new DefaultWebFilterChain(filters, handler, iterator.previous(), chain);
   }
   return chain;
}

         最终调用的构造函数如下

private DefaultWebFilterChain(List allFilters, WebHandler handler,
      @Nullable WebFilter currentFilter, @Nullable DefaultWebFilterChain chain) {

   this.allFilters = allFilters;
   this.currentFilter = currentFilter;
   this.handler = handler;
   this.chain = chain;
}

5. ShenyuWebHandler 的执行过程

5.1 ShenyuWebHandler的注册

        如阅读前准备知识所述,ShenyuWebHandler 在 ShenyuConfiguration类中作为一个bean被注册进spring。方法参数上第一个入参是plugins,关于它是如何获取到的,参见扩展问题。

@Bean("webHandler")
public ShenyuWebHandler shenyuWebHandler(final ObjectProvider> plugins, final ShenyuConfig config) {
    List pluginList = plugins.getIfAvailable(Collections::emptyList);
    List shenyuPlugins = pluginList.stream()
            .sorted(Comparator.comparingInt(ShenyuPlugin::getOrder)).collect(Collectors.toList());
    shenyuPlugins.forEach(shenyuPlugin -> LOG.info("load plugin:[{}] [{}]", shenyuPlugin.named(), shenyuPlugin.getClass().getName()));
    return new ShenyuWebHandler(shenyuPlugins, config);
}

5.2 ShenyuWebHandler的Handle方法

        由源码可知,ShenyuWebHandler的handle方法内部调用了DefaultShenyuPluginChain的execute方法。

@Override
public Mono handle(@NonNull final ServerWebExchange exchange) {
    Mono execute = new DefaultShenyuPluginChain(plugins).execute(exchange);
    if (scheduled) {
        return execute.subscribeOn(scheduler);
    }
    return execute;
}

5.3 DefaultShenyuPluginChain.execute()方法逻辑

        execute()方法迭代插件列表,并判断插件是否跳过,如果跳过就执行下一个插件,否则执行插件。神禹网关的调用链就是这样组织的。

@Override
public Mono execute(final ServerWebExchange exchange) {
    return Mono.defer(() -> {
        if (this.index < plugins.size()) {
            ShenyuPlugin plugin = plugins.get(this.index++);
            boolean skip = plugin.skip(exchange);
            if (skip) {
                return this.execute(exchange);
            }
            return plugin.execute(exchange, this);
        }
        return Mono.empty();
    });
}

6 扩展问题

        1. applicationContext是如何获取的

        2. ShenyuConfiguration注册ShenyuWebHandler时,插件是如何获取到的

        我会在下一章分享这两个问题。

你可能感兴趣的:(shenyu探秘,java,html,servlet)