Soul网关源码阅读(十)自定义简单插件编写
Soul网关源码阅读(十)自定义简单插件编写
简介
综合前面所分析的插件处理流程相关知识,此次我们来编写自定义的插件:统计请求在插件链中的经历时长
编写准备
首先我们先探究一下,一个Plugin是如何加载到上篇文章分析中的 plugins 中的,plugins 代码如下:
我们查看下 plugins 的值,发现global也在里面,也就是所有的plugin都是在里面
public class SoulConfiguration {@Bean("webHandler")public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);// global plugin 也在里面final List<SoulPlugin> soulPlugins = pluginList.stream().sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));return new SoulWebHandler(soulPlugins);}
}
在上面的调用栈已经中断了,一直翻不到什么有用的东西,换换思路,我们在 globalPlugin 构造函数上打上断点,重启
启动后,果然调用栈更新,我们看看调用栈,看到上面的 SoulConfiguration 调用是在 globalPlugin 之前,所以没啥有用的东西
我们查看下 globalPlugin 构造函数的调用触发上一节,发现是下面这个: GlobalPluginConfiguration
@Configuration
@ConditionalOnClass(GlobalPlugin.class)
public class GlobalPluginConfiguration {@Beanpublic SoulPlugin globalPlugin(final SoulContextBuilder soulContextBuilder) {return new GlobalPlugin(soulContextBuilder);}
}
我们仔细看看这个类,它是Spring Configuration,生成bean后注入进去,后面Spring会自己进行操作装配之类
我们注意到这个bean返回的是 SoulPlugin ,还记得我们前面文章分析的所有Plugin都是继承于这个的,所以 List 就会自动装配到所有的 plugin。这个细节我也不是很懂,Spring还是不够熟系,后面需要补一补
但看到这,我们大致思路就有了:
- 1.写一个自定义插件
- 2.写一个自定义插件的Spring配置,注入进去
自定义插件编写
首先说明下,插件的编写应该遵循Soul网关的规范,还是应该写到Soul-Plugin这个模块中,但我们只是试验验证,就随意一点,直接写在Soul-Bootstrap中
PS:时间有点小紧张,研究规范编写也伤时间,下次一定
工程结构
此次需要编写两个文件:
- 自定义插件:TimeRecordPlugin
- 自定义插件配置:TimeRecordConfiguration
目录结构大致如下:直接在源码的Soul-Bootstrap模块下
├─src
│ ├─main
│ │ ├─java
│ │ │ └─org
│ │ │ └─dromara
│ │ │ └─soul
│ │ │ └─bootstrap
│ │ │ ├─configuration : 放入自定义插件配置
│ │ │ ├─filter
│ │ │ └─plugin :放入自定义插件
│ │ └─resources
自定义插件编写
首先继承 SoulPlugin ,这样能正常注入到datalist中
然后编写相应的处理函数,在处理函数中,我们在请求第一次进入到插件的时候,在exchange中放入当前的系统时间
模仿 WebClientResponsePlugin ,在plugin链执行返回后,我们取出之前的系统时间,用当前系统时间减去,得到请求在plugin链中的经历时长
order方面需要注意, globalPlugin的order为0,通过前面文章的分析,它进行的操作也不小,我们这个插件得在它前面,那我们的order就设置为-1
这样,一个自定义插件就写好了,大致代码如下:
import lombok.extern.slf4j.Slf4j;
import org.dromara.soul.plugin.api.SoulPlugin;
import org.dromara.soul.plugin.api.SoulPluginChain;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Slf4j
public class TimeRecordPlugin implements SoulPlugin {private final String TIME_RECORD = "time_record";@Overridepublic Mono<Void> execute(ServerWebExchange exchange, SoulPluginChain chain) {exchange.getAttributes().put(TIME_RECORD, System.currentTimeMillis());return chain.execute(exchange).then(Mono.defer(() -> {Long startTime = exchange.getAttribute(TIME_RECORD);if (startTime == null) {log.info("Get start time error");return Mono.empty();}long timeRecord = System.currentTimeMillis() - startTime;log.info("Plugin time record: " + timeRecord + " ms");return Mono.empty();}));}@Overridepublic int getOrder() {return -1;}
}
自定义插件配置
灰常的简单,我们不需要任何东西,所以没有啥参数传入,直接new一个返回即可,代码如下:
import org.dromara.soul.bootstrap.plugin.TimeRecordPlugin;
import org.dromara.soul.plugin.api.SoulPlugin;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@ConditionalOnClass(TimeRecordPlugin.class)
public class TimeRecordConfiguration {@Beanpublic SoulPlugin timeRecordPlugin() {return new TimeRecordPlugin();}
}
运行测试
我们把Soul-admin、Soul-Bootstrap、Soul-Example-Http给启动起来
访问:http://127.0.0.1:9195/http/order/findById?id=1111
查看日志,看到明显自定义插件的日志打印,NICE!
o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://192.168.101.104:8188/order/findById?id=1111, retryTimes is 0
o.d.s.bootstrap.plugin.TimeRecordPlugin : Plugin time record: 9 ms
o.d.soul.plugin.base.AbstractSoulPlugin : resilience4j selector success match , selector name :http_limiter
o.d.soul.plugin.base.AbstractSoulPlugin : resilience4j rule success match , rule name :http_limiter
o.d.soul.plugin.base.AbstractSoulPlugin : divide selector success match , selector name :/http
o.d.soul.plugin.base.AbstractSoulPlugin : divide rule success match , rule name :/http/order/findById
o.d.s.plugin.httpclient.WebClientPlugin : The request urlPath is http://192.168.101.104:8188/order/findById?id=1111, retryTimes is 0
o.d.s.bootstrap.plugin.TimeRecordPlugin : Plugin time record: 10 ms
总结
Soul网关的主要处理分析基本快结束了,下篇写一个总结就准备开始分析另外一个重要的模块:数据同步
此篇的自定义插件编写还是比较简单,没有涉及到选择器和规则,但想做类似Divide之类的插件也不是不可以,那就直接把URI判断规则写死
因为请求的所有数据都可以获取的,不用到后台只是规则不能动态变化而已
有兴趣的老哥可以尝试写一下,还是挺有意思的,哈哈
Soul网关源码分析文章列表
Github
Soul源码阅读(一) 概览
Soul源码阅读(二)代码初步运行
Soul源码阅读(三)HTTP请求处理概览
Soul网关源码阅读(四)Dubbo请求概览
Soul网关源码阅读(五)请求类型探索
Soul网关源码阅读(六)Sofa请求处理概览
Soul网关源码阅读(七)限流插件初探
Soul网关源码阅读(八)路由匹配初探
Soul网关源码阅读(九)插件配置加载初探
Soul网关源码阅读番外篇(一) HTTP参数请求错误
掘金
Soul网关源码阅读(一) 概览
Soul网关源码阅读(二)代码初步运行
Soul网关源码阅读(三)请求处理概览
Soul网关源码阅读(四)Dubbo请求概览
Soul网关源码阅读(五)请求类型探索
Soul网关源码阅读(六)Sofa请求处理概览
Soul网关源码阅读(七)限流插件初探
Soul网关源码阅读(八)路由匹配初探
Soul网关源码阅读(九)插件配置加载初探
Soul网关源码阅读番外篇(一) HTTP参数请求错误
Soul网关源码阅读(十)自定义简单插件编写相关推荐
- Soul网关源码阅读(九)插件配置加载初探
Soul网关源码阅读(九)插件配置加载初探 简介 今日来探索一下插件的初始化,及相关的配置的加载 源码Debug 插件初始化 首先来到我们非常熟悉的插件链调用的类: SoulWebHa ...
- Soul网关源码阅读19-解析sign插件
sign插件是 soul网关自带的,用来对请求进行签名认证的插件,下面来解析一下sign插件. 一.环境搭建 soul-admin 开启 sign 插件:系统管理 --> 插件管理 soul-b ...
- Soul网关源码阅读(八)路由匹配初探
Soul网关源码阅读(八)路由匹配初探 简介 今日看看路由的匹配相关代码,查看HTTP的DividePlugin匹配 示例运行 使用HTTP的示例,运行Soul-Admin,Sou ...
- Soul网关源码阅读(七)限流插件初探
Soul网关源码阅读(七)限流插件初探 简介 前面的文章中对处理流程探索的差不多了,今天来探索下限流插件:resilience4j 示例运行 环境配置 启动下MySQL和redis d ...
- Soul网关源码阅读番外篇(一) HTTP参数请求错误
Soul网关源码阅读番外篇(一) HTTP参数请求错误 共同作者:石立 萧 * 简介 在Soul网关2.2.1版本源码阅读中,遇到了HTTP请求加上参数返回404的错误,此篇文章基于此进行探索 ...
- Soul 网关源码阅读(六)Sofa请求处理概览
Soul 网关源码阅读(六)Sofa请求处理概览 简介 今天来探索一下Sofa请求处理流程,看看和前面的HTTP.Dubbo有什么异同 Sofa示例运行 PS:如果请求加上参数运行不成功,请更 ...
- Soul网关源码阅读(六)请求类型探索
Soul网关源码阅读(六)请求类型探索 简介 在上几篇文章中分析了请求的处理流程,HTTP和RPC请求处理是互斥的,通过请求类型来判断,这篇文章来探索下请求类型的前世今生 源码分析 通 ...
- Soul 网关源码阅读(四)Dubbo请求概览
Soul 网关源码阅读(四)Dubbo请求概览 简介 本次启动一个dubbo服务示例,初步探索Soul网关源码的Dubbo请求处理流程 示例运行 环境配置 在Soul源码clone下来 ...
- Soul 网关源码阅读(二)代码初步运行
Soul 源码阅读(二)代码初步运行 简介 基于上篇:Soul 源码阅读(一) 概览,这部分跑一下Soul网关的示例 过程记录 现在我们可以根据地图,稍微探索一下周边,摸一摸 ...
最新文章
- python编程分析了一下高考那些事,发现了两个之最,原来是这样
- 中国呼吸道防护劳保用品市场投资效益与销售前景调研报告2022年
- 使用yarn安装quasar,quasar创建新项目 出错error eslint
- Cocos2d-x 3.2 学习笔记(四)学习打包Android平台APK!
- 复制密钥文件到另一台服务器,使用所有ssh密钥从第三台服务器在两台服务器之间远程复制...
- The NVIDIA driver on your system is too old (found version 10000). Please update your GPU driver by
- java编程 队列_5.1、顺序队列(java实现)
- 宝塔修改Nginx服务器类型,宝塔面板nginx更改日志格式的方法
- 华为P40 Pro相机高清细节图曝光:潜望式长焦镜头抢眼
- throw与throws
- html怎么给表格加a链接地址,html基础02-图片标签、绝/相对地址、表格的属性、链接的属性及链接的分类、name定义锚点的名称、编码...
- CentOS 7下Gitlab安装和迁移
- 【李宏毅2020 ML/DL】P26-33 Explainable ML
- matlab画x的1 3次方图像,Y=X的3次方的图像)
- 三月月赛 1005 wuli通通和doge(细节处理)
- Swiper实现手风琴式的图片展示
- python sklearn metrics,在Python中sklearn.metrics.mean_squared_error越大越好(否定)?
- 如何查看笔记本电脑的型号?
- 【违规举报】违规举报方法步骤
- Unity3D之矩阵运用
热门文章
- Can't use Subversion command line client:svn
- maven项目动态替换配置中的值
- 结组开发项目(TD学生助手)
- 关于解决Server Tomcat v9.0 Server at localhost failed to start的问题
- 【报告分享】2021中国人才趋势报告.pdf(附下载链接)
- 全球首发!计算机视觉Polygon Mesh Processing总结9——Triangle-Based Remeshing
- Airbnb搜索:深度学习排序算法如何进化?
- php 换一换 功能,vue换一换功能原型
- Java多线程同步屏障计算_Java多线程之CountDownLatch和CyclicBarrier同步屏障的使用
- 扫掠曲面二条引导线_说说国策下的三四线城市与会展