OpenTSDB/async详解

简介

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之后、以及计算平方。其过程的如下:

  1. callback输入ab;
  2. Deferred的callback1计算ab的和,并将和传递给callback2;
  3. 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个文件,如果有兴趣也可以下载来看看,嘻嘻==

你可能感兴趣的:(OpenTSDB/async详解)