soul网关学习十之apache-dubbo插件的调用

soul在处理对apache-dubbo的请求处理时会使用dubbo的泛化调用。主要流程如下;

1.在注册ApacheDubboPlugin时会注入ApacheDubboProxyServiceDubboMultiParameterResolveServiceImpl

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中怎么使用,没有做过多介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值