一.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插件:
@Override
public 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)) {
//正常请求需要构建soulContext
soulContext = 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插件
@Override
public 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的handle
return handleRequestBody(requestBodySpec, exchange, timeout, retryTimes, chain);
}
6.插件中主要介绍上面三个插件,经过插件后,就进入reactor.netty.http.client 进行异步调用下游服务接口.具体的源码信息在reactor-netty里面.本章节就不进行介绍了