使用cyclops-react在异步队列流上进行批处理

时间:2017-02-21 09:13:51

标签: batching cyclops-react

我正在尝试使用cyclops-react来根据大小来批处理队列中的元素,但也要按时,这样当没有元素时它就不会阻塞

也许功能不是我的预期,或者我做错了什么

完整代码(Groovy)与生产者在另一个线程中是这样的:

            Queue<String> queue = QueueFactories.<String>unboundedQueue().build();
    new Thread({
        while (true) {
            sleep(1000)
            queue.offer("New message " + System.currentTimeMillis());
        }
    }).start();

    StreamSource.futureStream(queue, new LazyReact(ThreadPools.queueCopyExecutor))
            .groupedBySizeAndTime(10,500,TimeUnit.MILLISECONDS)
            .forEach({i->println(i + " Batch Time: ${System.currentTimeMillis()}")})

输出结果为:

    [New message 1487673650332,  Batch Time: 1487673651356]
    [New message 1487673651348, New message 1487673652352,  Batch Time: 1487673653356]
    [New message 1487673653355, New message 1487673654357,  Batch Time: 1487673655362]
    [New message 1487673655362, New message 1487673656364,  Batch Time: 1487673657365]

但是我期待每个批次中有一个元素,因为提供的元素之间的延迟是10秒,但批处理是每半秒

我也尝试使用异步流(Groovy代码):

    Queue<String> queue = QueueFactories.<String>unboundedQueue().build();
    StreamSource.futureStream(queue, new LazyReact(ThreadPools.queueCopyExecutor))
            .async()
            .groupedBySizeAndTime(10, 500,TimeUnit.MILLISECONDS)
            .peek({i->println(i + "Batch Time: ${System.currentTimeMillis()}")}).run();

    while (true) {
        queue.offer("New message " + System.currentTimeMillis());
        sleep(1000)
    }

同样,它每2秒只进行一次批处理,有时每批等待两个元素,即使批处理中的超时是半秒:

    [New message 1487673877780, Batch Time: 1487673878819]
    [New message 1487673878811, New message 1487673879812, Batch Time: 1487673880815]
    [New message 1487673880814, New message 1487673881819, Batch Time: 1487673882823]
    [New message 1487673882823, New message 1487673883824, Batch Time: 1487673884828]
    [New message 1487673884828, New message 1487673885831, Batch Time: 1487673886835]

我用非未来的非懒惰流进行了第三次实验,这次它起作用了。

    Queue<String> queue = QueueFactories.<String>unboundedQueue().build();
    new Thread({
        while (true) {
            sleep(1000)
            queue.offer("New message " + System.currentTimeMillis());
        }
    }).start();

    queue.stream()
            .groupedBySizeAndTime(10,500,TimeUnit.MILLISECONDS)
            .forEach({i->println(i + " Batch Time " + System.currentTimeMillis())})

结果:

    [New message 1487673288017, New message 1487673289027,  Batch Time , 1487673289055]
    [New message 1487673290029,  Batch Time , 1487673290029]
    [New message 1487673291033,  Batch Time , 1487673291033]
    [New message 1487673292037,  Batch Time , 1487673292037]

为什么在使用未来的流时,批处理的行为似乎是错误的?

1 个答案:

答案 0 :(得分:0)

差异行为是由于一个错误降低了对async.Queue的FutureStreams进行分组的效率(基本上这意味着下一个结果出现在前一个500ms的限制内,而Stream会向队列询问另一个值和等到它到了)。这将在未来的cyclops-react版本中修复。

可以通过两种方式解决这个问题

  1. 在错误报告中使用Jesus Menendez建议的解决方法

    queue.stream()
         .groupedBySizeAndTime(batchSize, batchTimeoutMillis, TimeUnit.MILLISECONDS)
         .futureStream(new LazyReact(ThreadPools.getSequential()))
         .async()
         .peek(this::executeBatch)
         .run();
    
  2. 这可以避免导致两个值一起批处理的开销。

    1. 我们可以在500ms后超时(并且不要等到一个值到达队列中进行批处理),方法是使用streamBatch运算符

      Queue<String> queue = QueueFactories.<String>unboundedQueue().build();
      new Thread(()->{
          for(int i=0;i<10;i++){
      
              queue.offer("New message " + i);
              sleep(10000);
          }
          queue.close();
      }).start();
      
      long toRun = TimeUnit.MILLISECONDS.toNanos(500l);
      
      queue.streamBatch(new Subscription(), source->{
      
          return ()->{
              List<String> result = new ArrayList<>();
      
      
                 long start = System.nanoTime();
      
                     while (result.size() < 10 && (System.nanoTime() - start) < toRun) {
                         try {
                             String next = source.apply(1l, TimeUnit.MILLISECONDS);
                             if (next != null) {
                                 result.add(next);
                             }
                         }catch(Queue.QueueTimeoutException e){
      
                         }
      
      
                     }
      
              start=System.nanoTime();
      
              return result;
          };
      }).filter(l->l.size()>0)
        .futureStream(new LazyReact(ThreadPools.getSequential()))
              .async()
              .peek(System.out::println)
              .run();
      
    2. 在这种情况下,我们将始终在500毫秒后分组,而不是等到我们要求的值到达队列。