soul在处理对apache-dubbo的请求处理时会使用dubbo的泛化调用。主要流程如下;
1.在注册ApacheDubboPlugin
时会注入ApacheDubboProxyService
和DubboMultiParameterResolveServiceImpl
ApacheDubboProxyService
用于接收网关的请求再进行dubbo的泛化调用
DubboMultiParameterResolveServiceImpl
用于处理请求参数,用于dubbo的泛化调用
加载metadata时如果有dubbo类型元数据会在ApplicationConfigCache
内生成ReferenceConfig
用于对下游dubbo接口的实际调用
下面为具体代码
因为开启了dubbo插件,并且metadata中有dubbo接口配置,所以加载元数据时会调用ApacheDubboMetaDataSubscriber.onSubscribe
public void onSubscribe(final MetaData metaData) {
if (RpcTypeEnum.DUBBO.getName().equals(metaData.getRpcType())) {
MetaData exist = META_DATA.get(metaData.getPath());
if (Objects.isNull(META_DATA.get(metaData.getPath())) || Objects.isNull(ApplicationConfigCache.getInstance().get(metaData.getPath()))) {
// The first initialization 初始化
ApplicationConfigCache.getInstance().initRef(metaData);
} else {
// There are updates, which only support the update of four properties of serviceName rpcExt parameterTypes methodName,
// because these four properties will affect the call of Dubbo;
if (!metaData.getServiceName().equals(exist.getServiceName())
|| !metaData.getRpcExt().equals(exist.getRpcExt())
|| !metaData.getParameterTypes().equals(exist.getParameterTypes())
|| !metaData.getMethodName().equals(exist.getMethodName())) {
ApplicationConfigCache.getInstance().build(metaData);
}
}
META_DATA.put(metaData.getPath(), metaData);
}
}
第6行代码会调用ApplicationConfigCache.build
方法生成duboo泛化调用的ReferenceConfig
并且缓存到本地内存
public ReferenceConfig<GenericService> build(final MetaData metaData) {
ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
reference.setGeneric(true);
reference.setApplication(applicationConfig);
reference.setRegistry(registryConfig);
reference.setInterface(metaData.getServiceName());
reference.setProtocol("dubbo");
String rpcExt = metaData.getRpcExt();
DubboParamExtInfo dubboParamExtInfo = GsonUtils.getInstance().fromJson(rpcExt, DubboParamExtInfo.class);
if (Objects.nonNull(dubboParamExtInfo)) {
if (StringUtils.isNoneBlank(dubboParamExtInfo.getVersion())) {
reference.setVersion(dubboParamExtInfo.getVersion());
}
if (StringUtils.isNoneBlank(dubboParamExtInfo.getGroup())) {
reference.setGroup(dubboParamExtInfo.getGroup());
}
if (StringUtils.isNoneBlank(dubboParamExtInfo.getLoadbalance())) {
final String loadBalance = dubboParamExtInfo.getLoadbalance();
reference.setLoadbalance(buildLoadBalanceName(loadBalance));
}
if (StringUtils.isNoneBlank(dubboParamExtInfo.getUrl())) {
reference.setUrl(dubboParamExtInfo.getUrl());
}
Optional.ofNullable(dubboParamExtInfo.getTimeout()).ifPresent(reference::setTimeout);
Optional.ofNullable(dubboParamExtInfo.getRetries()).ifPresent(reference::setRetries);
}
Object obj = reference.get();
if (obj != null) {
log.info("init apache dubbo reference success there meteData is :{}", metaData.toString());
cache.put(metaData.getPath(), reference);
}
return reference;
}
可以看出 reference 保存了metadata中的信息,包括真实dubbo接口方法,zk地址,协议等信息。
3.当网关发起调用时与divide插件类似,先会调用GlobalPlugin
创建上下文, 然后调用BodyParamPlugin
根据ContentType封装请求参数
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
final ServerHttpRequest request = exchange.getRequest();
final SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
if (Objects.nonNull(soulContext) && RpcTypeEnum.DUBBO.getName().equals(soulContext.getRpcType())) {
MediaType mediaType = request.getHeaders().getContentType();
ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);
if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
return body(exchange, serverRequest, chain);
}
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) {
return formData(exchange, serverRequest, chain);
}
return query(exchange, serverRequest, chain);
}
return chain.execute(exchange);
}
随后调用ApacheDubboPlugin
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
String body = exchange.getAttribute(Constants.DUBBO_PARAMS);
SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
assert soulContext != null;
MetaData metaData = exchange.getAttribute(Constants.META_DATA);
if (!checkMetaData(metaData)) {
assert metaData != null;
log.error(" path is :{}, meta data have error.... {}", soulContext.getPath(), metaData.toString());
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
Object error = SoulResultWrap.error(SoulResultEnum.META_DATA_ERROR.getCode(), SoulResultEnum.META_DATA_ERROR.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
if (StringUtils.isNoneBlank(metaData.getParameterTypes()) && StringUtils.isBlank(body)) {
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
Object error = SoulResultWrap.error(SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getCode(), SoulResultEnum.DUBBO_HAVE_BODY_PARAM.getMsg(), null);
return WebFluxResultUtils.result(exchange, error);
}
// 使用 dubboProxyService 调用dubbo真实服务地址
final Mono<Object> result = dubboProxyService.genericInvoker(body, metaData, exchange);
return result.then(chain.execute(exchange));
}
dubboProxyService.genericInvoker()
方法
public Mono<Object> genericInvoker(final String body, final MetaData metaData, final ServerWebExchange exchange) throws SoulException {
// issue(https://github.com/dromara/soul/issues/471), add dubbo tag route
String dubboTagRouteFromHttpHeaders = exchange.getRequest().getHeaders().getFirst(Constants.DUBBO_TAG_ROUTE);
if (StringUtils.isNotBlank(dubboTagRouteFromHttpHeaders)) {
RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, dubboTagRouteFromHttpHeaders);
}
// 获取 加载元数据时缓存的包含真实dubbo服务接口的 ReferenceConfig
ReferenceConfig<GenericService> reference = ApplicationConfigCache.getInstance().get(metaData.getPath());
if (Objects.isNull(reference) || StringUtils.isEmpty(reference.getInterface())) {
ApplicationConfigCache.getInstance().invalidate(metaData.getPath());
reference = ApplicationConfigCache.getInstance().initRef(metaData);
}
GenericService genericService = reference.get();
Pair<String[], Object[]> pair;
if (ParamCheckUtils.dubboBodyIsEmpty(body)) {
pair = new ImmutablePair<>(new String[]{}, new Object[]{});
} else {
// 构建dubbo所需的参数
pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes());
}
// 真实调用dubbo接口
CompletableFuture<Object> future = genericService.$invokeAsync(metaData.getMethodName(), pair.getLeft(), pair.getRight());
return Mono.fromFuture(future.thenApply(ret -> {
if (Objects.isNull(ret)) {
ret = Constants.DUBBO_RPC_RESULT_EMPTY;
}
// 异步处理dubbo接口响应
exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, ret);
exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName());
return ret;
})).onErrorMap(exception -> exception instanceof GenericException ? new SoulException(((GenericException) exception).getExceptionMessage()) : new SoulException(exception));
}
最后使用DubboResponsePlugin
对dubbo响应进行处理
因为 ReferenceConfig
我也不是很了解,暂时没有介绍,只是查看了soul中怎么使用,没有做过多介绍。