Dubbo的服务调用流程

目录

服务的消费

createProxy

protocol.refer

RegistryProtocol.refer

doRefer

cluster.join

消费端调用的过程

InvokerInvocationHandler.invoke

服务降级

AbstractClusterInvoker.invoke

容错机制

负载均衡

服务端接收消息的处理流程

HeaderExchangeHandler.received

invoker.invoke(inv)


如果要实现服务的消费,那么应该需要实现下面的过程

  • 生成远程服务的代理
  • 获得目标服务的url地址
  • 实现远程网络通信
  • 实现负载均衡
  • 实现集群容错

服务的消费

@Reference()的初始化是在ReferenceConfig.get()->init()->createProxy(map)。

createProxy

createProxy(Map<String, String> map)这个方法主要做了下面这些事情。

  1. 判断是否为本地调用,如果是则使用injvm协议进行调用
  2. 判断是否为点对点调用,如果是则把url保存到urls集合中,如果url为1,进入步骤4,如果urls>1,则执行5
  3. 如果是配置了注册中心,遍历注册中心,把url添加到urls集合,url为1,进入步骤4,如果urls>1,则执行5
  4. 直连构建invoker
  5. 构建invokers集合,通过cluster合并多个invoker
  6. 最后调用 ProxyFactory 生成代理类
private T createProxy(Map<String, String> map) {
        if (shouldJvmRefer(map)) {//判断是否是在同一个jvm进程中调用
            URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
            invoker = REF_PROTOCOL.refer(interfaceClass, url);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        } else {
            urls.clear();
            //url 如果不为空,说明是点对点通信
            if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
                String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        URL url = URL.valueOf(u);
                        if (StringUtils.isEmpty(url.getPath())) {
                            url = url.setPath(interfaceName);
                        }
                        if (UrlUtils.isRegistry(url)) {
                            urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // assemble URL from register center's configuration
                // if protocols not injvm checkRegistry
                if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
                    checkRegistry();
                    //根据注册中心配置进行解析得到URL
                    //registry://ip:port/org.apache.dubbo.service.RegsitryService
                    List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
                    if (CollectionUtils.isNotEmpty(us)) {
                        for (URL u : us) {
                            URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
                            if (monitorUrl != null) {
                                map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                            }
                            urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        }
                    }
                    //如果没有配置注册中心,则报错
                    if (urls.isEmpty()) {
                        throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                    }
                }
            }
            //如果值配置了一个注册中心或者一个服务提供者,直接使用refprotocol.refer
            if (urls.size() == 1) {
                invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {//遍历urls生成多个invoker
                    invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
                    if (UrlUtils.isRegistry(url)) {
                        registryURL = url; // use last registry url
                    }
                }
                if (registryURL != null) { // registry url is available
                    // for multi-subscription scenario, use 'zone-aware' policy by default
                    //如果registryUrl不为空,构建静态directory
                    URL u = registryURL.addParameterIfAbsent(CLUSTER_KEY, ZoneAwareCluster.NAME);
                    // The invoker wrap relation would be like: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
                    // 通过Cluster将多个invoker合并
                    invoker = CLUSTER.join(new StaticDirectory(u, invokers));
                } else { // not a registry url, must be direct invoke.
                    invoker = CLUSTER.join(new StaticDirectory(invokers));
                }
            }
        }

        if (shouldCheck() && !invoker.isAvailable()) {
            invoker.destroy();
            throw new IllegalStateException("Failed to check the status of the service "
                    + interfaceName
                    + ". No provider available for the service "
                    + (group == null ? "" : group + "/")
                    + interfaceName +
                    (version == null ? "" : ":" + version)
                    + " from the url "
                    + invoker.getUrl()
                    + " to the consumer "
                    + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        }
        if (logger.isInfoEnabled()) {
            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
        }
        /**
         * @since 2.7.0
         * ServiceData Store
         */
        String metadata = map.get(METADATA_KEY);
        WritableMetadataService metadataService = WritableMetadataService.getExtension(metadata == null ? DEFAULT_METADATA_STORAGE_TYPE : metadata);
        if (metadataService != null) {
            URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
            metadataService.publishServiceDefinition(consumerURL);
        }
        // create service proxy
        return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
    }

protocol.refer

通过指定的协议来调用refer生成一个invoker对象,它是一个代理对象。那么在当前的消费端而言,invoker主要用于执行远程调用。

在上面的代码中可以看到REF_PROTOCOL.refer(interfaceClass, url),再看看REF_PROTOCOL的定义

private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

很明显,这是一个自适应扩展点,根据当前的协议url:registry://...所以调用RegistryProtocol.refer

RegistryProtocol.refer

  • 组装注册中心协议的url
  • 判断是否配置legroup,如果有,则cluster=getMergeableCluster(),构建invoker
  • doRefer构建invoker
@Override
    @SuppressWarnings("unchecked")
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        //根据配置的协议,生成注册中心的url: zookeeper://
        url = getRegistryUrl(url);
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        // 解析group参数,根据group决定cluster的类型
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
        String group = qs.get(GROUP_KEY);
        if (group != null && group.length() > 0) {
            if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        return doRefer(cluster, registry, type, url);
    }

doRefer

用来构建invoker

  • 构建一个RegistryDirectory
  • 构建一个consumer://协议的地址注册到注册中心
  • 订阅zookeeper中节点的变化
  • 调用cluster.join方法返回invoker
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        //RegistryDirectory初始化
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        // all attributes of REFER_KEY
        Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters());
        //注册consumer://协议的url
        URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (directory.isShouldRegister()) {
            directory.setRegisteredConsumerUrl(subscribeUrl);
            registry.register(directory.getRegisteredConsumerUrl());
        }
        directory.buildRouterChain(subscribeUrl);
        //订阅事件监听
        directory.subscribe(toSubscribeUrl(subscribeUrl));

        //构建invoker
        Invoker<T> invoker = cluster.join(directory);
        List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
        if (CollectionUtils.isEmpty(listeners)) {
            return invoker;
        }

        RegistryInvokerWrapper<T> registryInvokerWrapper = new RegistryInvokerWrapper<>(directory, cluster, invoker, subscribeUrl);
        for (RegistryProtocolListener listener : listeners) {
            listener.onRefer(this, registryInvokerWrapper);
        }
        return registryInvokerWrapper;
    }

cluster.join

从上面可以看出返回的invoker是通过cluster.join来获取的。cluster其实是在RegistryProtocol中通过set方法完成依赖注入的

Invoker invoker=cluster.join(directory)
@SPI(FailoverCluster.NAME)
public interface Cluster {

    /**
     * Merge the directory invokers to a virtual invoker.
     *
     * @param <T>
     * @param directory
     * @return cluster invoker
     * @throws RpcException
     */
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;

}

在动态适配的类中会基于extName,选择一个合适的扩展点进行适配,由于默认情况下cluster:failover,所以getExtension("failover")理论上应该返回FailOverCluster。但实际上,这里做了包装MockClusterWrapper(FailOverCluster)

所以这里返回的invoker,应该是MockClusterWrapper(FailOverCluster(directory))

在回到ReferenceConfig.createProxy

在最后一行有这样的一句代码

return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));

proxyFactory又是一个自适应扩展点生成动态代理类,调用JavassistProxyFactory.getProxy

@Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

意味着后续发起服务调用的时候,会由InvokerInvocationHandler来进行处理,客户端调用的本质上也是一个动态代理类。

消费端调用的过程

消费者初始化完成之后,会生成一个proxy,而这个proxy本质上是一个动态代理类。

@Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

这个invoker实际上是:MockClusterWrapper(FailoverCluster(directory)),然后通过InvokerInvocationHandler做了一层包装变成了InvokerInvocationHandler(MockClusterWrapper(FailoverCluster(directory)))。

所以会一层层的调用最终调用真正的方法。

InvokerInvocationHandler.invoke

这个方法主要判断当前调用的远程方法,如果是tostring、hashcode、equals,就直接返回否则,调用invoker.invoke,进入到 MockClusterWrapper.invoke 方法

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }

        return invoker.invoke(createInvocation(method, args)).recreate();
    }

服务降级

MockClusterInvoker.invoke

  1. 是否客户端强制配置了mock调用,那么在这种场景中主要可以用来解决服务端还没开发好的时候直接使用本地数据进行测试
  2. 是否出现了异常,如果出现异常则使用配置好的Mock类来实现服务的降级
@Override
    public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;
        //从url中获得MOCK_KEY对应的value
        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
        //如果没有配置mock,则直接传递给下个invoker调用
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
            //no mock
            result = this.invoker.invoke(invocation);
        } else if (value.startsWith("force")) {//如果强制为本地调用,则执行mockInvoke
            if (logger.isWarnEnabled()) {
                logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
            }
            //force:direct mock
            result = doMockInvoke(invocation, null);
        } else {
            //fail-mock
            try {
                result = this.invoker.invoke(invocation);
            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                }
                
                if (logger.isWarnEnabled()) {
                    logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
                }
                //如果远程调用出现异常,则使用Mock进行处理
                result = doMockInvoke(invocation, e);
            }
        }
        return result;
    }

AbstractClusterInvoker.invoke

根据链路原本应该调用的是FailoverClusterInvoke.invoke,但是这里是调用父类的invoke,在调用FailoverClusterInvoke.doInvoker

@Override
    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();

        // binding attachments into invocation.
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }

        List<Invoker<T>> invokers = list(invocation);
        //初始化负载均衡机制
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        //调用容错逻辑
        return doInvoke(invocation, invokers, loadbalance);
    }

    protected void checkWhetherDestroyed() {
        if (destroyed.get()) {
            throw new RpcException("Rpc cluster invoker for " + getInterface() + " on consumer " + NetUtils.getLocalHost()
                    + " use dubbo version " + Version.getVersion()
                    + " is now destroyed! Can not invoke any more.");
        }
    }

容错机制

FailoverClusterInvoker.doInvoke这里实现了容错机制,failover是失败重试。

基本逻辑是:

  • 获得重试的次数,并且进行循环
  • 获得目标服务,并且记录当前已经调用过的目标服务防止下次继续将请求发送过去
  • 如果执行成功,则返回结果
  • 如果出现异常,判断是否为业务异常,如果是则抛出,否则,进行下一次重试

  • 这里的  Invoker 是  Provider 的一个可调用  Service 的抽象, Invoker 封装了  Provider 地址及  Service 接口信息
  • Directory 代表多个  Invoker ,可以把它看成  List<Invoker> ,但与  List 不同的是,它的值可能是动态变化的,比如注册中心推送变更
  • Cluster 将  Directory 中的多个  Invoker 伪装成一个  Invoker ,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
  • Router 负责从多个  Invoker 中按路由规则选出子集,比如读写分离,应用隔离等
  • LoadBalance 负责从多个  Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选

负载均衡

FailoverClusterInvoker.doInvoke中的select(loadbalance, invocation, copyInvokers,invoked)

在调用服务之前,需要通过select选择一个合适的服务进行调用,而这个选择的过程其实就是负载均衡的实现

所有负载均衡实现类均继承自 AbstractLoadBalance,该类实现了 LoadBalance 接口,并封装了一些公共的逻辑。

AbstractLoadBalance.doSelect

@Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        if (CollectionUtils.isEmpty(invokers)) {
            return null;
        }
        // 如果 invokers 列表中仅有一个 Invoker,直接返回即可,无需进行负载均衡
        if (invokers.size() == 1) {
            return invokers.get(0);
        }
        //调用 doSelect 方法进行负载均衡
        return doSelect(invokers, url, invocation);
    }

负载均衡的子类实现有四个,默认情况下是RandomLoadBalance

服务端接收消息的处理流程

进入NettyServerHandler.channelRead(),客户端传过来的消息会在这里进行处理。

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
        try {
            handler.received(channel, msg);
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

handler.received(channel, msg);中的handler是经过层层包装的。

DubboProtocol.export一路向下可以找到DubboProtocol.createServer,在这里我们可以看到这样一句代码,对requestHandler进行封装

server = Exchangers.bind(url, requestHandler);

HeaderExchanger.bind

@Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

Transporters.bind

public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        if (handlers == null || handlers.length == 0) {
            throw new IllegalArgumentException("handlers == null");
        }
        ChannelHandler handler;
        if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }
        return getTransporter().bind(url, handler);
    }

再层层往下,最后可得到

MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler

  • MultiMessageHandler: 复合消息处理
  • HeartbeatHandler:心跳消息处理,接收心跳并发送心跳响应
  • AllChannelHandler:业务线程转化处理器,把接收到的消息封装成ChannelEventRunnable可执行任务给线程池处理
  • DecodeHandler:业务解码处理器
  • HeaderExchangeHandler:响应处理

HeaderExchangeHandler.received

直接看HeaderExchangeHandler.received

public void received(Channel channel, Object message) throws RemotingException {
    channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
    ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
    try {
        if (message instanceof Request) {
            // 处理请求
            Request request = (Request) message;
            if (request.isEvent()) {
            	// 处理事件消息
                handlerEvent(channel, request);
            } else {
            	// 处理普通请求
                if (request.isTwoWay()) {
                	// 双向通信
                    Response response = handleRequest(exchangeChannel, request);
                    channel.send(response);
                } else {
                	// 单向通信,无需返回请求结果
                    handler.received(exchangeChannel, request.getData());
                }
            }
        } else if (message instanceof Response) {
        	// 处理响应
            handleResponse(channel, (Response) message);
        } else if (message instanceof String) {
            .....
        } else {
            handler.received(exchangeChannel, message);
        }
    } finally {
        HeaderExchangeChannel.removeChannelIfDisconnected(channel);
    }
}

Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
    Response res = new Response(req.getId(), req.getVersion());
    // 请求错误,返回错误消息
    if (req.isBroken()) {
        Object data = req.getData();

        String msg;
        if (data == null) msg = null;
        else if (data instanceof Throwable) msg = StringUtils.toString((Throwable) data);
        else msg = data.toString();
        res.setErrorMessage("Fail to decode request due to: " + msg);
        res.setStatus(Response.BAD_REQUEST);

        return res;
    }
    // 调用服务
    Object msg = req.getData();
    try {
        // 继续调用,这里的handler又是哪一个呢?
        Object result = handler.reply(channel, msg);
        res.setStatus(Response.OK);
        res.setResult(result);
    } catch (Throwable e) {
        res.setStatus(Response.SERVICE_ERROR);
        res.setErrorMessage(StringUtils.toString(e));
    }
    return res;
}

这里的 handler.reply 是DubboProtocol.createServer方法中传入的requestHandler。

private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
    
    public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
        if (message instanceof Invocation) {
            Invocation inv = (Invocation) message;
            // 获取服务端Invoker对象
            Invoker<?> invoker = getInvoker(channel, inv);
            //如果是callback 需要处理高版本调用低版本的问题
            if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){
                String methodsStr = invoker.getUrl().getParameters().get("methods");
                boolean hasMethod = false;
                if (methodsStr == null || methodsStr.indexOf(",") == -1){
                    hasMethod = inv.getMethodName().equals(methodsStr);
                } else {
                    String[] methods = methodsStr.split(",");
                    for (String method : methods){
                        if (inv.getMethodName().equals(method)){
                            hasMethod = true;
                            break;
                        }
                    }
                }
            }
            RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
            // 调用invoke方法,最终会调用服务端实现的具体服务方法
            return invoker.invoke(inv);
        }
        throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
    }

	// 忽略其它方法
};
getInvoker(channel, inv)中DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);

exporterMap就是发布过程保存的。在服务发布时,实际上是把invoker包装成了DubboExpoter。然后放入到exporterMap中。

invoker.invoke(inv)

invoker.invoke(inv)中的invoker就是getInvoker(channel, inv)获取的1,那么这个invoker又是什么呢?

invoker=ProtocolFilterWrapper(InvokerDelegate(DelegateProviderMetaDataInvoker(AbstractProxyInvoker)))

在AbstractProxyInvoker里面,doInvoker本质上调用的是wrapper.invokeMethod()

在这里调用真正的方法进行返回。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值