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<List<ShenyuPlugin>> plugins, final ShenyuConfig config) {List<ShenyuPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);List<ShenyuPlugin> 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<WebFluxProperties> propsProvider) {HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build(); //HttpHandler生成方式WebFluxProperties properties = propsProvider.getIfAvailable();if (properties != null && StringUtils.hasText(properties.getBasePath())) {Map<String, HttpHandler> 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为ShenyuWebHandlerWebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class), context);List<WebFilter> webFilters = context.getBeanProvider(WebFilter.class).orderedStream().collect(Collectors.toList());builder.filters(filters -> filters.addAll(webFilters));List<WebExceptionHandler> 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<WebFilter> filters) {super(handler);this.chain = new DefaultWebFilterChain(handler, filters);
}

4.5 DefaultWebFilterChain filter链的构造

DefaultWebFilterChain构造函数如下所示。

public DefaultWebFilterChain(WebHandler handler, List<WebFilter> 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

initChain的源代码如下所示

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

最终调用的构造函数如下

private DefaultWebFilterChain(List<WebFilter> 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<List<ShenyuPlugin>> plugins, final ShenyuConfig config) {List<ShenyuPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);List<ShenyuPlugin> 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<Void> handle(@NonNull final ServerWebExchange exchange) {Mono<Void> execute = new DefaultShenyuPluginChain(plugins).execute(exchange);if (scheduled) {return execute.subscribeOn(scheduler);}return execute;
}

5.3 DefaultShenyuPluginChain.execute()方法逻辑

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

@Override
public Mono<Void> 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时,插件是如何获取到的

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

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

  1. 调用链系列四:调用链上下文传递

    在之前的调用链系列文章中,我们已经对调用链进行了详细介绍,相信大家已经对调用链技术有了基本的了解. 其实,在调用链的绘制过程中,调用链上下文的传递非常值得关注.各个节点在获取上层上下文后生成新的上下文 ...

  2. 小程聊微服务-自己动手扩展分布式调用链

    一.说在前面 微服务是当下最火的词语,现在很多公司都在推广微服务,当服务越来越多的时候,我们是否会纠结以下几个问题: 面对一笔超时的订单,究竟是哪一步处理时间超长呢? 数据由于并发莫名篡改,到底都谁有 ...

  3. 程超:手把手教你动手扩展分布式调用链

    一.说在前面 微服务是当下最火的词语,现在很多公司都在推广微服务,当服务越来越多的时候,我们是否会纠结以下几个问题: 面对一笔超时的订单,究竟是哪一步处理时间超长呢? 数据由于并发莫名篡改,到底都谁有 ...

  4. 2w字长文,让你瞬间拥有「调用链」开发经验

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 很多同学表示,对于微服务中常用的调用链功能的原理,感觉很模糊.本文 ...

  5. 如何让控件span的id调用ajax_微服务架构之「 调用链监控 」

    「 调用链监控 」是在微服务兴起后才有的一种新流行的监控模式.因为在我们传统单体应用的项目中,不存在服务链/调用链的概念,所以也就根本没有调用链监控的需求了. 当我们开始微服务架构之后,我们的很多服务 ...

  6. java高可用grpc_GRPC java 分布式调用链跟踪实践

    Opentracing基本模型 image.png 如图,在跟踪链中有以下几个比较重要的数据结构和概念: span:标识一次分布式调用,其自身包含了id,parentId(指向上级Span的id), ...

  7. SpringBoot实战(十六):集成Skywalking调用链监控系统

    强烈推荐一个大神的人工智能的教程:http://www.captainbed.net/zhanghan [前言] Skywalking做为生产级的调用链监控工具,不仅提供了丰富的监控系统,而且通过字节 ...

  8. 13、Nepxion Discovery 之 全链路调用链监控

    在进行微服务调用的时候,为了系统的高可用性,不仅需要进行灰度发布验证服务的可用性.同时对于服务健康的监控也是很重要的一环.Nepxion Discovery 在这方面也有监控方面的集成,包含以下几个方 ...

  9. 微服务架构之「 调用链监控 」

    「 调用链监控 」是在微服务兴起后才有的一种新流行的监控模式.因为在我们传统单体应用的项目中,不存在服务链/调用链的概念,所以也就根本没有调用链监控的需求了. 当我们开始微服务架构之后,我们的很多服务 ...

  10. 微服务调用链日志追踪分析

    一.技术原理 1.1 背景 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元.由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位.主要体现在,一个 ...

最新文章

  1. 产生BFC环境的几种方式
  2. SQL查询四舍五入 解决方法
  3. Linux下Tomcat多部署形式,让每个项目维护起来互不影响
  4. guava之preconditions
  5. 如何接入虹软免费人脸识别SDK
  6. 模板—扩展GCD*2
  7. Android Animation学习(六) View Animation介绍
  8. 通向架构师的道路(第五天)之tomcat集群-群猫乱舞
  9. java实现表达式求值_如何编写一个高效的Java表达式求值程序
  10. SSH服务器能够允许root用户远程登录
  11. mysqldump备份所有数据库,恢复单个库的场景预演
  12. JetBrains:webstrom关闭重复代码校验
  13. C++ tbb::atomic<bool> 声明、读取load、重新赋值store
  14. 理解Python中的with…as…语法
  15. 简单游戏(easygame)
  16. JAVA分布式快速开发基础平台iBase4J
  17. java编程规范换行_Java源代码的换行规则
  18. 我的个人知识管理工具软件
  19. MATLAB计算卫星相对位置、速度和加速度
  20. AtCoder Beginner Contest 121

热门文章

  1. pdfjs转图片_PDF转图片,PDF转JPG/PNG,完全由JS实现-阿里云开发者社区
  2. troubleshooting-1
  3. 阿里巴巴等大厂的 Java岗位要求是什么?
  4. 10个CSS技巧,极大提升用户体验
  5. 使用toUpperCase toLowerCase getBytes方法实现一串字母字符的大小写转换
  6. 大数据,云计算,物联网和移动互联网与传统互联网,主要有什么关系?
  7. 火狐浏览器打印网页不全_打印网页显示不全,求助
  8. 四则运算生成程序(基于控制台)
  9. 微星如何于BIOS中开启/关闭AMD虚拟化技术
  10. Word上下标快捷键