Dubbo异步调用(七)

本文详细介绍了Dubbo的异步调用机制,包括2.0.6+版本和2.7.0之后的变化。在2.0.6+版本中,异步调用是非阻塞的NIO调用,用户线程立即返回,通过Future等待结果。2.7.0后,引入CompletableFuture作为基础,允许更灵活的回调处理。配置和代码示例展示了如何实现异步调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、参考

异步调用:http://dubbo.apache.org/en-us/docs/user/demos/async-call.html

2、异步调用

2.1、Dubbo2.0.6+的异步调用

这里说的异步调用应该是非阻塞的NIO调用,一个线程可同时并发调用多个远程服务,每个服务的调用都是非阻塞的,线程立即返回。

/user-guide/images/future.jpg

上图中主要关注一下userThread的行为,用户发出调用后,IOThread会在上下文中设置Future。用户从RpcContext中取得Future,然后wait这个Future其它的事情都由IOThread完成。

总之,用户发出调用后会立刻返回,而不是调用在服务端执行完代码、返回结果后返回。用户返回后可以去做点其它事情,比如调用另外一个服务,然后回头等待前一个调用完成。从上图可以看出,异步调用完全是Consumer端的行为。

配置:

<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
      <dubbo:method name="findFoo" async="true" />
</dubbo:reference>
<dubbo:reference id="barService" interface="com.alibaba.bar.BarService">
      <dubbo:method name="findBar" async="true" />
</dubbo:reference>

async=true表示异步调用,可以看到配置发生在Consumer端,能精确到方法。

方法异步调用后,Consumer端的代码写法:

// 此方法应该返回Foo,但异步后会立刻返回NULL
fooService.findFoo(fooId);
// 立刻得到当前调用的Future实例,当发生新的调用时这个东西将会被覆盖
Future<Foo> fooFuture = RpcContext.getContext().getFuture();

// 调用另一个服务的方法
barService.findBar(barId);
// 立刻得到当前调用的Future
Future<Bar> barFuture = RpcContext.getContext().getFuture();

// 此时,两个服务的方法在并发执行
// 等待第一个调用完成,线程会进入Sleep状态,当调用完成后被唤醒。
Foo foo = fooFuture.get();
// 同上
Bar bar = barFuture.get();
// 假如第一个调用需要等待5秒,第二个等待6秒,则整个调用过程完成的时间是6秒。

理解的状态是要有一个多路复用器,同时等待所有的调用,谁就绪就处理谁。Dubbo中是按顺序等待,如果后边的调用已经调用完成,前边的没有完成,那么后边的调用会被前边的卡住,得不到及时处理。

配置是否等待IOThread发送完Request后再返回:

  • sent="true" ,等待请求发送出去后再往回,如果发送失败直接抛出异常。
  • sent="false" ,将调用交给IOThread后立即返回。实际这个时候请求进入到IOThread的队列,排除等着被发送出去。
<dubbo:method name="findFoo" async="true" sent="true" />

如果对返回结果没有兴趣:

<dubbo:method name="findFoo" async="true" return="false" />

2.2、Dubbo2.7.0之后的异步调用

从v2.7.0开始,Dubbo的所有异步编程接口开始以CompletableFuture为基础,感觉这种方式还不成熟,官方文档也没什么资料,很多重要问题没有说明,看一下就好了。

服务端接口的定义要改变,主要指返回值类型:

public interface AsyncService {
    CompletableFuture<String> sayHello(String name);
}

Consumer端引用服务:

// 调用直接返回CompletableFuture
CompletableFuture<String> future = asyncService.sayHello("async call request");
// 增加回调
future.whenComplete((v, t) -> {
    if (t != null) {
        t.printStackTrace();
    } else {
        System.out.println("Response: " + v);
    }
});
// 早于结果输出
System.out.println("Executed before response return.");

调用发起后,立刻得到一个future。然后通过Lamda表达式向此future增加回调。将远程接口真正返回后,由此回调处理返回结果。

这里有一个问题没讲明白,回调的执行必然不是当前线程,Consumer部应该也会配置一个线程池,由线程池里边的线程执行回调应该。

上边的异步调用没有涉及到RpcContext,另外一个异步调用的方法就是使用RpcContext,与上边方法的区别在于Consumer端的配置与代码,这种风格与旧版本非常像。

配置:

<dubbo:reference id="asyncService" interface="org.apache.dubbo.samples.governance.api.AsyncService">
      <dubbo:method name="sayHello" async="true" />
</dubbo:reference>

代码:

// 此调用会立即返回null
asyncService.sayHello("world");
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
CompletableFuture<String> helloFuture = RpcContext.getContext().getCompletableFuture();
// 为Future添加回调
helloFuture.whenComplete((retValue, exception) -> {
    if (exception == null) {
        System.out.println(retValue);
    } else {
        exception.printStackTrace();
    }
});

还有一种方法:

CompletableFuture<String> future = RpcContext.getContext().asyncCall(
    () -> {
        asyncService.sayHello("oneway call request1");
    }
);

future.get();

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值