soul 网关源码解析
一.soul网关引入的依赖分析
从上图可以看到我红线划分五个依赖区域
1.soul-common包:这里不是很重要,我们大概看一下他的作用就好了
从上图中可以看出,这个包里主要定义了一个常量,枚举类,配置类,自定义的DTO对象
2.soul-metrics这些依赖包主要是监控暴露出来的代码,这里也不是很重要,先略过
3.最重要的就是插件部分 soul-plugin依赖包,这里包含了你项目中会用到的插件源码
4.websocket和sync部分依赖包: 主要是网关和soul后台管理系统同步配置信息的
5.soul-web包也很重要,里面是soul网关的请求入口类,和一些拦截器,配置信息类.
二.请求流程:
可以看到exchange里面包含着request信息和response信息.
plugins插件里面会告诉你需要执行的有多少个插件
1.首先请求会到SoulWebHandler类里面的execute方法中
public Mono<Void> execute(final ServerWebExchange exchange) {return Mono.defer(() -> {//循环执行插件if (this.index < plugins.size()) {SoulPlugin plugin = plugins.get(this.index++);//判断插件是否跳过执行Boolean skip = plugin.skip(exchange);if (skip) {return this.execute(exchange);}//调用插件执行逻辑return plugin.execute(exchange, this);}return Mono.empty();});}
2.执行插件代码
可以看到spring cloud项目一共需要执行10个插件,其中3,4,7是我自定义的插件
3.GlobalPlugin插件:
@Overridepublic Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {final ServerHttpRequest request = exchange.getRequest();final HttpHeaders headers = request.getHeaders();final String upgrade = headers.getFirst("Upgrade");SoulContext soulContext;//判断是否是websocket同步的配置请求if (StringUtils.isBlank(upgrade) || !"websocket".equals(upgrade)) {//正常请求需要构建soulContextsoulContext = builder.build(exchange);} else {final MultiValueMap<String, String> queryParams = request.getQueryParams();soulContext = transformMap(queryParams);}exchange.getAttributes().put(Constants.CONTEXT, soulContext);return chain.execute(exchange);}
继续看DefaultSoulContextBuilder类中的build方法
public SoulContext build(final ServerWebExchange exchange) {ServerHttpRequest request = exchange.getRequest();String path = request.getURI().getPath();//根据请求的路径匹配到配置的元数据MetaData metaData = MetaDataCache.getInstance().obtain(path);if (Objects.nonNull(metaData) && metaData.getEnabled()) {exchange.getAttributes().put(Constants.META_DATA, metaData);}//构建本次请求的SoulContext上下文对象return Optional.ofNullable(metaData).map(e -> decoratorMap.get(e.getRpcType())).orElse(decoratorMap.get(RpcTypeEnum.HTTP.getName())).decorator(buildDefault(request), metaData);}
4.spring cloud plugin插件
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {if (Objects.isNull(rule)) {return Mono.empty();} else {SoulContext soulContext = (SoulContext)exchange.getAttribute("context");assert soulContext != null;//取出后台管理系统配置的选择器规则对象SpringCloudRuleHandle ruleHandle = (SpringCloudRuleHandle)GsonUtils.getInstance().fromJson(rule.getHandle(), SpringCloudRuleHandle.class);//取出后台管理系统配置的选择器对象SpringCloudSelectorHandle selectorHandle = (SpringCloudSelectorHandle)GsonUtils.getInstance().fromJson(selector.getHandle(), SpringCloudSelectorHandle.class);if (!StringUtils.isBlank(selectorHandle.getServiceId()) && !StringUtils.isBlank(ruleHandle.getPath())) {//调用spring cloud ribbon对象从eureka注册中心获取注册服务实例ServiceInstance serviceInstance = this.loadBalancer.choose(selectorHandle.getServiceId());if (Objects.isNull(serviceInstance)) {Object error = SoulResultWrap.error(SoulResultEnum.SPRINGCLOUD_SERVICEID_IS_ERROR.getCode(), SoulResultEnum.SPRINGCLOUD_SERVICEID_IS_ERROR.getMsg(), (Object)null);return WebFluxResultUtils.result(exchange, error);} else {//根据服务实例负载均衡获取真正的请求服务的URL信息URI uri = this.loadBalancer.reconstructURI(serviceInstance, URI.create(soulContext.getRealUrl()));//构建真正的请求URL,这里有BUG,需要注意String realURL = this.buildRealURL(uri.toASCIIString(), exchange.getRequest().getURI().getQuery());exchange.getAttributes().put("httpUrl", realURL);exchange.getAttributes().put("httpTimeOut", ruleHandle.getTimeout());return chain.execute(exchange);}} else {Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_CONFIG_SPRINGCLOUD_SERVICEID.getCode(), SoulResultEnum.CANNOT_CONFIG_SPRINGCLOUD_SERVICEID.getMsg(), (Object)null);return WebFluxResultUtils.result(exchange, error);}}}
5.webClientPlugin插件
@Overridepublic Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);assert soulContext != null;String urlPath = exchange.getAttribute(Constants.HTTP_URL);if (StringUtils.isEmpty(urlPath)) {Object error = SoulResultWrap.error(SoulResultEnum.CANNOT_FIND_URL.getCode(), SoulResultEnum.CANNOT_FIND_URL.getMsg(), null);return WebFluxResultUtils.result(exchange, error);}//获取后台管理系统配置的超时时间long timeout = (long) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_TIME_OUT)).orElse(3000L);//重试时间int retryTimes = (int) Optional.ofNullable(exchange.getAttribute(Constants.HTTP_RETRY)).orElse(0);log.info("The request urlPath is {}, retryTimes is {}", urlPath, retryTimes);HttpMethod method = HttpMethod.valueOf(exchange.getRequest().getMethodValue());WebClient.RequestBodySpec requestBodySpec = webClient.method(method).uri(urlPath);//构建request的handlereturn handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain);}
6.插件中主要介绍上面三个插件,经过插件后,就进入reactor.netty.http.client 进行异步调用下游服务接口.具体的源码信息在reactor-netty里面.本章节就不进行介绍了
soul 网关源码解析相关推荐
- Soul网关源码解析目录
Soul网关源码解析目录 Soul网关源码解析文章列表 对用Java写的高性能网关:Soul,进行一波学习和研究,下面是相关的文章记录 掘金 了解与初步运行 Soul网关源码解析(一) 概览 ...
- Soul网关源码解析(二)代理Http请求
如何读开源项目:对着文档跑demo,对着demo看代码,懂一点就开始试,有问题了问社区. 文章目录 今日目标: 一.从官方文档开始 1.接入说明: 2.网关需要引入代理插件 3.Http 服务接入网关 ...
- Soul网关源码解析(三)代理Dubbo服务
文章目录 目标 一.使用 soul 代理 dubbo 服务 1.dubbo 服务接入网关 1.1 springboot 项目接入方式 1.2 spring 项目接入方式 2.配置 dubbo 插件 3 ...
- soul网关源码解析-环境搭建
项目功能简介 支持各种语言(http协议),支持 dubbo,springcloud协议. 插件化设计思想,插件热插拔,易扩展. 灵活的流量筛选,能满足各种流量控制. 内置丰富的插件支持,鉴权,限流, ...
- Soul网关源码阅读(八)路由匹配初探
Soul网关源码阅读(八)路由匹配初探 简介 今日看看路由的匹配相关代码,查看HTTP的DividePlugin匹配 示例运行 使用HTTP的示例,运行Soul-Admin,Sou ...
- Soul网关源码阅读(七)限流插件初探
Soul网关源码阅读(七)限流插件初探 简介 前面的文章中对处理流程探索的差不多了,今天来探索下限流插件:resilience4j 示例运行 环境配置 启动下MySQL和redis d ...
- Soul 网关源码阅读(六)Sofa请求处理概览
Soul 网关源码阅读(六)Sofa请求处理概览 简介 今天来探索一下Sofa请求处理流程,看看和前面的HTTP.Dubbo有什么异同 Sofa示例运行 PS:如果请求加上参数运行不成功,请更 ...
- Soul网关源码阅读(十)自定义简单插件编写
Soul网关源码阅读(十)自定义简单插件编写 简介 综合前面所分析的插件处理流程相关知识,此次我们来编写自定义的插件:统计请求在插件链中的经历时长 编写准备 首先我们先探究一下,一个P ...
- Soul网关源码阅读(九)插件配置加载初探
Soul网关源码阅读(九)插件配置加载初探 简介 今日来探索一下插件的初始化,及相关的配置的加载 源码Debug 插件初始化 首先来到我们非常熟悉的插件链调用的类: SoulWebHa ...
最新文章
- python中接口测试垃圾数据如何清理_一个六年经验的python后端是怎么学习用java写API的(2)Extracter,微信文章抓取清洗入库...
- PostgreSQL增强版命令行客户端(pgcli)
- 如何更新Postgresql的Jsonb数组
- 全新iPhone“Pro”命名要实锤:新增配色是“原谅色”本色吧?
- shell foreach 拼接字符串_FIND_IN_SET 及IN 处理逗号间隔的字符串参数
- nedc和epa续航里程什么意思_了解 NEDC 之后 我发现电动车的续航还是得实测
- Xcode 真机 iPhone is not available 及 is busy 解决
- tcp连接python_python网络编程--TCP连接的三次握手(三报文握手)与四次挥手
- hdu 3987 - 最小割最少割边
- 苹果手机如何关闭自动续费_手机APP会员自动续费怎么办?教你一招快速关闭!...
- 2018工业互联网峰会在京召开
- 继电器控制模块的一些知识
- chrom,firefox,ie不能上网,百度浏览器却可以。。。
- google翻译破解大综合
- SqlDataReader转换为DataTable
- 通用pe 装linux,U盘通用PE安装CentOS
- 42步进电机与57步进电机
- 开源:基于COF屏的雕刻机方案控制开源GRBL_ESP32
- nlp学习--专家系统总结-专家系统基础
- 非参数估计-parzen窗估计和k近邻估计