soul源码阅读 soul数据同步之dubbo插件原理解析

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&register.ip=192.168.0.103&remote.timestamp=1611789931688&retries=2&side=consumer&timeout=10000&timestamp=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调用。具体细节可以看看代码,可以参考本文的思路一步步断点调试。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值