zoukankan      html  css  js  c++  java
  • dubbo入门之异步调用

    dubbo默认使用同步的方式调用。但在有些特殊的场景下,我们可能希望异步调用dubbo接口,从而避免不必要的等待时间,这时候我们就需要用到异步。那么dubbo的异步是如何实现的呢?下面就来看看这个问题
    异步方法配置:

    <dubbo:reference cache="lru" id="demoService" interface="com.ping.chen.dubbo.service.DemoService" timeout="5000">
        <dubbo:method name="sayHello" async="true"/>
    </dubbo:reference>
    

    底层的异步处理实现在DubboInvoker的doInvoke方法中,源码如下:

    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        忽略Attachment设置。。。
        try {
            // 是否异步执行
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);//是否单向执行
            int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);//接口超时时间,默认1秒
            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) {
            //异常处理。。。
        } 
    }
    

    可以看到,如果异步执行,会直接返回一个空的RpcResult,然后用户如果需要异步执行结果,可以从RpcContext中的Future中去获取,直接用RpcContext.getContext().getFuture().get();就可以获取到执行结果。那么RpcContext是如何保证当前线程可以拿到执行结果呢?答案是ThreadLocal。我们来看看RpcContext源码如下:

    public class RpcContext {
    
    	private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() {
    		@Override
    		protected RpcContext initialValue() {
    			return new RpcContext();
    		}
    	};
    	public static RpcContext getContext() {
    		return LOCAL.get();
    	}
                。。。。。。
    }
    

    可以看到RpcContext 内部是使用ThreadLocal来实现的。

    异步可能遇到的坑

    上面这种配置有一个坑,可能你会像下面这样使用异步:

    @RestController
    public class DemoController {
    	@Reference(timeout = 5000)
    	DemoProvider demoProvider;
    
    	@RequestMapping("/demo")
    	public void demo() throws ExecutionException, InterruptedException {
    		demoProvider.sayHello("world");
    		System.out.println(">>>>>>>>>>" + RpcContext.getContext().getFuture().get());
    	}
    }
    

    然后请求demo接口的时候,很不幸,你将会收到一个NullPointException,这因为我们只在消费者端配置了异步执行,但服务端执行的时候是同步的,所以我们从RpcContext中获取到的Future是空的,正确的异步配置应该是:
    直接去掉消费者端的

    <dubbo:method name="sayHello" async="true"/>
    

    配置,然后在服务提供者端做如下配置:

    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.ping.chen.dubbo.provider.DemoProvider" ref="demoProvider" >
        <dubbo:method name="sayHello" async="true"/>
    </dubbo:service>
    

    如此,便可以实现异步了

  • 相关阅读:
    设计模式--策略模式
    安装PLSQLDeveloper
    oracle11g数据库安装
    tcp和udp的头部信息
    多线程之间实现通讯
    并发编程之多线程线程安全
    多线程快速入门
    帝国cms定时自动执行刷新任务插件DoTimeRepage
    华为S5700交换机初始化和配置SSH和TELNET远程登录方法
    织梦php7数据库备份无结果BUG修复方法
  • 原文地址:https://www.cnblogs.com/canmeng-cn/p/10554625.html
Copyright © 2011-2022 走看看