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<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。
之后它又做了三件事情
- 找到所有的webFilters;
- 找到所有的exceptionHandlers;
- 找到所有的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(神禹)网关调用链及组装过程相关推荐
- 调用链系列四:调用链上下文传递
在之前的调用链系列文章中,我们已经对调用链进行了详细介绍,相信大家已经对调用链技术有了基本的了解. 其实,在调用链的绘制过程中,调用链上下文的传递非常值得关注.各个节点在获取上层上下文后生成新的上下文 ...
- 小程聊微服务-自己动手扩展分布式调用链
一.说在前面 微服务是当下最火的词语,现在很多公司都在推广微服务,当服务越来越多的时候,我们是否会纠结以下几个问题: 面对一笔超时的订单,究竟是哪一步处理时间超长呢? 数据由于并发莫名篡改,到底都谁有 ...
- 程超:手把手教你动手扩展分布式调用链
一.说在前面 微服务是当下最火的词语,现在很多公司都在推广微服务,当服务越来越多的时候,我们是否会纠结以下几个问题: 面对一笔超时的订单,究竟是哪一步处理时间超长呢? 数据由于并发莫名篡改,到底都谁有 ...
- 2w字长文,让你瞬间拥有「调用链」开发经验
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 很多同学表示,对于微服务中常用的调用链功能的原理,感觉很模糊.本文 ...
- 如何让控件span的id调用ajax_微服务架构之「 调用链监控 」
「 调用链监控 」是在微服务兴起后才有的一种新流行的监控模式.因为在我们传统单体应用的项目中,不存在服务链/调用链的概念,所以也就根本没有调用链监控的需求了. 当我们开始微服务架构之后,我们的很多服务 ...
- java高可用grpc_GRPC java 分布式调用链跟踪实践
Opentracing基本模型 image.png 如图,在跟踪链中有以下几个比较重要的数据结构和概念: span:标识一次分布式调用,其自身包含了id,parentId(指向上级Span的id), ...
- SpringBoot实战(十六):集成Skywalking调用链监控系统
强烈推荐一个大神的人工智能的教程:http://www.captainbed.net/zhanghan [前言] Skywalking做为生产级的调用链监控工具,不仅提供了丰富的监控系统,而且通过字节 ...
- 13、Nepxion Discovery 之 全链路调用链监控
在进行微服务调用的时候,为了系统的高可用性,不仅需要进行灰度发布验证服务的可用性.同时对于服务健康的监控也是很重要的一环.Nepxion Discovery 在这方面也有监控方面的集成,包含以下几个方 ...
- 微服务架构之「 调用链监控 」
「 调用链监控 」是在微服务兴起后才有的一种新流行的监控模式.因为在我们传统单体应用的项目中,不存在服务链/调用链的概念,所以也就根本没有调用链监控的需求了. 当我们开始微服务架构之后,我们的很多服务 ...
- 微服务调用链日志追踪分析
一.技术原理 1.1 背景 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元.由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位.主要体现在,一个 ...
最新文章
- 产生BFC环境的几种方式
- SQL查询四舍五入 解决方法
- Linux下Tomcat多部署形式,让每个项目维护起来互不影响
- guava之preconditions
- 如何接入虹软免费人脸识别SDK
- 模板—扩展GCD*2
- Android Animation学习(六) View Animation介绍
- 通向架构师的道路(第五天)之tomcat集群-群猫乱舞
- java实现表达式求值_如何编写一个高效的Java表达式求值程序
- SSH服务器能够允许root用户远程登录
- mysqldump备份所有数据库,恢复单个库的场景预演
- JetBrains:webstrom关闭重复代码校验
- C++ tbb::atomic<bool> 声明、读取load、重新赋值store
- 理解Python中的with…as…语法
- 简单游戏(easygame)
- JAVA分布式快速开发基础平台iBase4J
- java编程规范换行_Java源代码的换行规则
- 我的个人知识管理工具软件
- MATLAB计算卫星相对位置、速度和加速度
- AtCoder Beginner Contest 121