简介
OpenTSDB/async中的Deferred可以很轻松的构建异步处理链,opentsdb的源码中也大量用到这个工具。若想使用这个工具,在maven中加上这个依赖即可:
com.stumbleupon
async
1.4.0
这篇文章将会通过例子来介绍async的全部特性。
场景1
现在我们有一个场景,a、b是两个整数,现在需要求得a、b之和的平方。
(其实要实现这个场景很简单,直接计算 (a+b)*2即可,但是此处是为了讲解async的特性而设计的一个简单的场景。
这个场景的代码也很简单,如下:
Deferred deferred = new Deferred<>()
.addCallback(s -> {
int[] ab = (int[])s;
int sum = ab[0] + ab[1];
System.out.println("ab之和为:" + sum);
return 1;
}).addCallback(s -> {
int square = s*s;
System.out.println("ab之和的平方为:"+square);
return square;
});
int[] ab = {4,6};
deferred.callback(ab);
输出结果如下:
ab之和为:10
ab之和的平方为:1
如代码所示,新建Deferred的增加了两个callback,两个callback分别是计算ab之后、以及计算平方。其过程的如下:
- callback输入ab;
- Deferred的callback1计算ab的和,并将和传递给callback2;
- Deferred的callback2计算ab之和的平方。
这个场景,除了上面方式之外,还可以以另一种方式:
Deferred deferredSum = new Deferred<>()
.addCallback(s -> {
int[] ab = (int[])s;
int sum = ab[0] + ab[1];
System.out.println("ab之和为:" + sum);
return 1;
});
Deferred deferredSquare = new Deferred<>()
.addCallback(s -> {
int sum = (int)s;
int square = sum*sum;
System.out.println("ab之和的平方为:"+square);
return square;
});
Deferred deferred = deferredSum.chain(deferredSquare);
int[] ab = {4,6};
deferred.callback(ab);
当然输出结果会和上面一样,因为两种方法是等价的,Deferred#chain()方法允许我们将多个deferred组成一个链。
可以看到,callback里面可以写一些语句,其实callback里面也是可以定义一个新的Deferred,并可以将其返回。同样是这个场景,代码如下:
Deferred deferred = new Deferred<>()
.addCallbackDeferring(s -> {
Deferred deferredInner = new Deferred<>()
.addCallback(sInner -> {
int[] ab = (int[])sInner;
int sum = ab[0] + ab[1];
System.out.println(Thread.currentThread().getName()+" ab之和为:" + sum);
return sum;
});
Executors.newSingleThreadExecutor().execute(() -> {
deferredInner.callback(s);
});
return deferredInner;
}).addCallback(s -> {
int square = s*s;
System.out.println(Thread.currentThread().getName()+" ab之和的平方为:"+square);
return square;
});
int[] ab = {4,6};
deferred.callback(ab);
输出结果如下:
pool-1-thread-1 ab之和为:10
main ab之和的平方为:100
由此可知,Deferred是可以进行嵌套操作的。
场景2
现在我们是需要计算 (10)/n+1 的结果为多少,同样也是举一个比较简单的例子去对Deferred的用法做一个理解。
这个场景中 n 取值为0时,当然是会报错的,所以需要考虑到这个情况。计算代码如下:
Deferred deferred = new Deferred<>()
.addCallback( s -> {
final int num = 10;
final int sInt = (int)s;
try {
final int result = num/sInt;
System.out.println("Callback1计算结果为:"+result);
return result;
} catch (Exception e) {
System.out.println("Callback1计算出错:"+e.getMessage());
return e;
}
} )
.addCallbacks(
s -> {
int sInt = (int)s;
final int result = ++sInt;
System.out.println("Callback2计算结果为:"+result);
return result;
},
s -> {
final Exception e = (Exception)s;
System.out.println("Callback2接收到错误信息:"+e.getMessage());
return null;
}
);
final int n = 2;
//final int n = 0;
deferred.callback(n);
其输出结果如下:
n=2时:
Callback1计算结果为:5
Callback2计算结果为:6
n=0时:
Callback1计算出错:/ by zero
Callback2接收到错误信息:/ by zero
可以看到Deferred是有容错能力,因为 Deferred#addCallbacks(cb,eb) 方法有两个参数,分别是callback和errorback。
- 当Callback1计算成功并且没有报错时,会将result传递给Callback2,此时是一个正常的back,所做会执行 Deferred#addCallbacks(cb,eb) 的第一个参数——cb。
- 当Callback1计算出现错误时,会将其错误传递给Callback2,此时执行的就是 Deferred#addCallbacks(cb,eb) 的第二个参数——eb。
场景3
假设现在我们需要计算 (a+b)*(c+d) 的值,那么我们可以用 deferredAb 计算 a+b,deferredCd 去计算 c+d,并收集 deferredAb 和 deferredCd 的计算结果,具体代码如下:
final int[] ab = {1,2};
Deferred deferredAb = Deferred.fromResult(ab)
.addCallback( s -> {
final int result = s[0] + s[1];
System.out.println("ab之和为:"+result);
return result;
} );
final int[] cd = {3,4};
Deferred deferredCd = Deferred.fromResult(cd)
.addCallback( s -> {
final int result = s[0] + s[1];
System.out.println("cd之和为:"+result);
return result;
} );
final List> deferreds = new ArrayList<>(2);
deferreds.add(deferredAb);
deferreds.add(deferredCd);
Deferred> deferred = Deferred.group(deferreds);
deferred.addCallback( s-> {
final int abSum = s.get(0);
final int cdSum = s.get(1);
final int result = abSum*cdSum;
System.out.println("abSum和cdSum之积为:"+result);
return result;
});
输出结果如下:
ab之和为:3
cd之和为:7
abSum和cdSum之积为:21
- Deferred#fromResult 方法可以构造一个已经有结果的 Deferred ,并且其会传递给后面的callback。类似的 Deferred#fromError 也可以构造一个有结果的Deferred,并且其会传递给后面的error back。
- Deferred.group 会可以将 Deferred 列表进行合并,并且会收集列表中的每个Deferred返回的值。官方的说法是当所有的Deferred有call back,可以简单地使用一个动作去处理。
这个例子中其实就是,deferred 会在 deferredAb 和 deferredCd 都 call back 进行相关的处理,这里的处理也就是计算 abSum 和 abSum 之积。
同样是这个例子,假设计算和数比较耗时,那么我们可以定义deferredAb和deferredCd 分别取计算ab之和,cd之和,并使用两个线程去进行计算,最后主线程等待两个线程的结果。
final int[] ab = {1,2};
Deferred deferredAb = new Deferred()
.addCallback( s -> {
Thread.sleep(5000);
final int result = s[0] + s[1];
System.out.println("ab之和为:"+result);
return result;
} );
final int[] cd = {3,4};
Deferred deferredCd = new Deferred()
.addCallback( s -> {
Thread.sleep(5000);
final int result = s[0] + s[1];
System.out.println("cd之和为:"+result);
return result;
} );
final List> deferreds = new ArrayList<>(2);
deferreds.add(deferredAb);
deferreds.add(deferredCd);
Deferred> deferred = Deferred.group(deferreds);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> deferredAb.callback(ab));
executorService.execute(() -> deferredCd.callback(cd));
final List sums = deferred.joinUninterruptibly();
final int abSum = sums.get(0);
final int cdSum = sums.get(1);
final int result = abSum*cdSum;
System.out.println("abSum和cdSum之积为:"+result);
executorService.shutdown();
输出如下:
ab之和为:3
cd之和为:7
abSum和cdSum之积为:21
Deferred#joinUninterruptibly()方法是同步等待deferred的callback的结果。
总结
Deferred的用法基本就是这些,∩_∩,其实比较简单,其实现的代码也比较精辟,源码也就7个文件,如果有兴趣也可以下载来看看,嘻嘻==