AlibabaDubboPlugin的类图如下,继承了AubstactSoulPlugin类,实现了SoulPlugin接口
调用dubbo的链路(调用地址:http://localhost:9195/dubbo/findById?userId=123):
SoulWebHandler.execute()->AbstractSoulPlugin.execute()->AlibabaDubboPlugin.doExecute()->AlibabaDubboProxyService.genericInvoker()
在SoulWebHandler.execute会循环查看pluginList。我发现soul的很多代码都会做循环查询,虽然说现在插件的数量并不多,循环 20次以内就可以解决,但是如果以后插件数量从10变成100,那这些大量的循环应该要优化才对。
下图是AbstractSoulPlugin.execute(),非常奇怪为什么会进来,因为像rate_limiter这个插件我就没有打开。
以下代码是AbstractSoulPlugin.execute(),加了一些注释帮助理解
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final SoulPluginChain chain) {
String pluginName = named();
//PluginData(id=6, name=dubbo, config={"register":"zookeeper://localhost:2181"}, role=1, enabled=true)
final PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
//这里会判断插件是否打开,如果没打开就跳过
if (pluginData != null && pluginData.getEnabled()) {
//SelectorData(id=1350569414272282624, pluginId=6, pluginName=dubbo, name=/dubbo, matchMode=0, type=1, sort=1, enabled=true, loged=true, continued=true, handle=null, conditionList=[ConditionData(paramType=uri, operator=match, paramName=/, paramValue=/dubbo/**)])
//这里从缓存SELECTOR_MAP这个hashmap取出SelectorData
final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
//判空处理
if (CollectionUtils.isEmpty(selectors)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
//SelectorData(id=1350569414272282624, pluginId=6, pluginName=dubbo, name=/dubbo, matchMode=0, type=1, sort=1, enabled=true, loged=true, continued=true, handle=null, conditionList=[ConditionData(paramType=uri, operator=match, paramName=/, paramValue=/dubbo/**)])
final SelectorData selectorData = matchSelector(exchange, selectors);
if (Objects.isNull(selectorData)) {
return handleSelectorIsNull(pluginName, exchange, chain);
}
//日志
selectorLog(selectorData, pluginName);
//RuleData(id=1350569414372945920, name=/dubbo/findById, pluginName=dubbo, selectorId=1350569414272282624, matchMode=0, sort=1, enabled=true, loged=true, handle={"retries":0,"loadBalance":"random","timeout":3000}, conditionDataList=[ConditionData(paramType=uri, operator==, paramName=/, paramValue=/dubbo/findById)])
//从缓存中获取规则列表
final List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
if (CollectionUtils.isEmpty(rules)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
RuleData rule;
if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
//获取最后一个
rule = rules.get(rules.size() - 1);
} else {
rule = matchRule(exchange, rules);
}
//RuleData(id=1350569414372945920, name=/dubbo/findById, pluginName=dubbo, selectorId=1350569414272282624, matchMode=0, sort=1, enabled=true, loged=true, handle={"retries":0,"loadBalance":"random","timeout":3000}, conditionDataList=[ConditionData(paramType=uri, operator==, paramName=/, paramValue=/dubbo/findById)])
if (Objects.isNull(rule)) {
return handleRuleIsNull(pluginName, exchange, chain);
}
ruleLog(rule, pluginName);
return doExecute(exchange, chain, selectorData, rule);
}
return chain.execute(exchange);
}
以下代码是AlibabaDubboPlugin.doExecute(),加了点注释帮助理解
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) {
//body:{"userId":"132"} 获取传递的参数
String body = exchange.getAttribute(Constants.DUBBO_PARAMS);
//SoulContext(module=dubbo, method=org.dromara.soul.examples.dubbo.api.service.DubboTestService, rpcType=dubbo, httpMethod=GET, sign=null, timestamp=null, appKey=null, path=/dubbo/findById, contextPath=/dubbo, realUrl=null, dubboParams=null, startDateTime=2021-01-28T07:42:38.503)
SoulContext soulContext = exchange.getAttribute(Constants.CONTEXT);
assert soulContext != null;
//MetaData(id=null, appName=dubbo, contextPath=/dubbo, path=/dubbo/findById, rpcType=dubbo, serviceName=org.dromara.soul.examples.dubbo.api.service.DubboTestService, methodName=findById, parameterTypes=java.lang.String, rpcExt={"group":"","version":"","loadbalance":"random","retries":2,"timeout":10000,"url":""}, enabled=true)
//获取元数据
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);
}
Object result = alibabaDubboProxyService.genericInvoker(body, metaData);
if (Objects.nonNull(result)) {
exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, result);
} else {
exchange.getAttributes().put(Constants.DUBBO_RPC_RESULT, Constants.DUBBO_RPC_RESULT_EMPTY);
}
exchange.getAttributes().put(Constants.CLIENT_RESPONSE_RESULT_TYPE, ResultEnum.SUCCESS.getName());
return chain.execute(exchange);
}
以下代码是AlibabaDubboProxyService.genericInvoker(),加了点注释帮助理解
/**
* Generic invoker object.
* 反射生成类
* @param body the body
* @param metaData the meta data
* @return the object
* @throws SoulException the soul exception
*/
public Object genericInvoker(final String body, final MetaData metaData) throws SoulException {
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);
}
//反射DUBBO RPC 获取接口等数据
//interface com.alibaba.dubbo.rpc.service.GenericService -> zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=soul_proxy&bean.name=org.dromara.soul.examples.dubbo.api.service.DubboTestService&check=false&dubbo=2.0.2&generic=true&interface=org.dromara.soul.examples.dubbo.api.service.DubboTestService&loadbalance=random&methods=findById,insert,findAll&pid=92735&protocol=dubbo®ister.ip=192.168.0.103&remote.timestamp=1611789931688&retries=2&side=consumer&timeout=10000×tamp=1611789934821
GenericService genericService = reference.get();
try {
Pair<String[], Object[]> pair;
if (ParamCheckUtils.dubboBodyIsEmpty(body)) {
pair = new ImmutablePair<>(new String[]{}, new Object[]{});
} else {
//参数及数据
pair = dubboParamResolveService.buildParameter(body, metaData.getParameterTypes());
}
//dubbo反射传入参数及方法名调用
return genericService.$invoke(metaData.getMethodName(), pair.getLeft(), pair.getRight());
} catch (GenericException e) {
log.error("dubbo invoker have exception", e);
throw new SoulException(e.getExceptionMessage());
}
}
到这里,基本上就了解了为什么能通过soul调用dubbo。大致的思路就是通过调用的地址获取存在zookeeper对应的dubbo的接口和方法,通过反射进行RPC调用。具体细节可以看看代码,可以参考本文的思路一步步断点调试。