美文网首页
2-2 异步调用

2-2 异步调用

作者: xhrg | 来源:发表于2018-12-29 14:31 被阅读0次

使用

异步调用的使用非常简单,只需要如下配置 async="true" 便是异步调用,默认同步。

<dubbo:reference id="personService"
        interface="com.wang.dubbo.api.PersonService" async="true"/>

异步调用指的是,服务消费者调用后直接返回null,但是服务请求依然会到服务消费者端。

原理

代码跟踪,从业务代码调用方法出debug往进走,一直沿着 调用链路的invoke往进走,直到如下调用。

  @Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
        inv.setAttachment(Constants.VERSION_KEY, version);
        
        ExchangeClient currentClient;
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
            if (isOneway) {
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {
                ResponseFuture future = currentClient.request(inv, timeout) ;
                RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
                return new RpcResult();
            } else {
                RpcContext.getContext().setFuture(null);
                return (Result) currentClient.request(inv, timeout).get();
            }
        } catch (TimeoutException e) {
            throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        } catch (RemotingException e) {
            throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

上面这段代码的关键点在于这里:异步如下:
ResponseFuture future = currentClient.request(inv, timeout) ;
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
同步如下:
return (Result) currentClient.request(inv, timeout).get();

说明:
currentClient.request,这个方法会返回一个ResponseFuture,如果没有调用get则是异步,调用get则是同步。

DefaultFuture(ResponseFuture)

请看这个类的get方法。

 public Object get(int timeout) throws RemotingException {
        if (timeout <= 0) {
            timeout = Constants.DEFAULT_TIMEOUT;
        }
        if (! isDone()) {
            long start = System.currentTimeMillis();
            lock.lock();
            try {
                while (! isDone()) {
                    done.await(timeout, TimeUnit.MILLISECONDS);
                    if (isDone() || System.currentTimeMillis() - start > timeout) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            if (! isDone()) {
                throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
            }
        }
        return returnFromResponse();
 }

这个代码是阻塞的核心在于isDone方法,而isDone方法的释放点在于。doReceived方法。在doReceived上debug后我们能看到这个类的调用来自于netty的调用。

333333333.png

发送的DefaultFuture和回调过来的DefaultFuture如何绑定。

请看如下代码,代码来自DefaultFuture:FUTURES是本地的缓存类。

    public static void received(Channel channel, Response response) {
        try {
            DefaultFuture future = FUTURES.remove(response.getId());
            if (future != null) {
                future.doReceived(response);
            } else {
                logger.warn("The timeout response finally returned at " 
                            + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) 
                            + ", response " + response 
                            + (channel == null ? "" : ", channel: " + channel.getLocalAddress() 
                                + " -> " + channel.getRemoteAddress()));
            }
        } finally {
            CHANNELS.remove(response.getId());
        }
    }

相关文章

  • 2-2 异步调用

    使用 异步调用的使用非常简单,只需要如下配置 async="true" 便是异步调用,默认同步。 异步调用指的...

  • Spring中使用@Async异步调用方法

    摘要 异步调用传统SSM项目实现 异步调用SpringBoot实现 Async简介: 异步方法调用使用场景:处理日...

  • Dubbo中的那些坑(二)异步调用

    Dubbo异步调用 Dubbo原生支持异步调用,但其中依然有坑。 异步调用依赖传递性 问题表现:如果consume...

  • 2018-03-13 Spring中的异步调用

    异步方法调用 异步方法调用或异步方法模式是(多线程)面向对象程序设计中用于异步调用对象的潜在的长期运行方法的一种设...

  • 04Vue的前后端交互

    Vue的前后端交互 Promise用法 异步调用 触发异步调用的方式定时任务Ajax事件函数 多次异步调用的依赖分...

  • 消息队列之异步消息基本概念以及ActiveMQ整合Spring常

    一 简介 (1)异步消息: 所谓异步消息,跟RMI远程调用、webservice调用是类似的,异步消息也是用于应用...

  • SpringBoot线程池异步调用

    异步调用介绍 异步调用异步调用就是在不阻塞主线程的情况下执行高耗时方法 常规异步通过开启新线程实现 在Spring...

  • BIO,NIO,AIO

    同步、异步、阻塞、非阻塞 同步与异步 同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。 异步...

  • 实现异步转同步

    极客时间-《Java并发编程实战》学习笔记 异步方法:调用方法,在方法中启动子线程异步调用:启动子线程调用方法异步...

  • SpringBoot+@Async注解一起用,速度提升100倍!

    简介: 异步调用几乎是处理高并发Web应用性能问题的万金油,那么什么是“异步调用”?“异步调用”对应的是“同步调用...

网友评论

      本文标题:2-2 异步调用

      本文链接:https://www.haomeiwen.com/subject/wqiulqtx.html