如何将轮询API转换为反应流

时间:2019-07-16 10:02:30

标签: java project-reactor

说我有一个具有以下签名的函数:

class Item {
  String name;
  Long id;
}
public Flux<Item> getNew(long id);

getNew()返回在id(0..N)之后添加的项目流。那么如何将其变成无限流?

是这样的:

public Flux<Item> observe(long id) {
    return Flux.interval(Duration.ofSeconds(1)).
             flatMap(counter -> getNew(id)); // <-- how to use last value from getNew flux as the new id                
}

我能够做到的唯一方法是使用某种类型的状态变量:

   public Flux<Long> observe(long id) {
     final AtomicLong last = new AtomicLong(id);
     return Flux.interval(Duration.ofSeconds(1)).
         flatMap(l -> getNew(last.get())).
         doOnNext(last::set);    
   }    

是否有更惯用的方法来做到这一点?我试图为此创建生成器,但是我不知道如何实现它。

1 个答案:

答案 0 :(得分:1)

如果您可以通过检查来确定Item发出的最后一个getNew,则可以使用.expand运算符:

    public Flux<Item> observe(long id) {
        return getNew(id)
                .expand(item -> isLast(item)
                        ? getNew(item.id)
                        : Flux.empty());
    }
    /**
     * @return true if the given item is the last item emitted by getNew
     */
    private boolean isLast(Item item) {
        return // ... snip ...
    }


如果您无法通过检查来识别最后一个Item,则必须使用状态变量。虽然,我建议使用.defer.repeat而不是.interval ...

    public Flux<Item> observe(long id) {
        final AtomicLong nextStartId = new AtomicLong(id);
        return Flux.defer(() -> getNew(nextStartId.get()))
                .doOnNext(item -> nextStartId.set(item.id))
                .repeat();
    }

不使用.interval的主要原因是:

  

如果未及时产生需求,则会发出onError信号

因此,如果API花费的时间太长,或者处理结果花费的时间太长,流将以错误结尾。长间隔可能不是问题,但是间隔相对较短(例如您的示例中为1秒),这可能是个问题。

如果要在每次重复迭代之前进行延迟,则可以将.repeatWhen与带有固定退避值的Reactor-Extra的Repeat配合使用。这将为您提供“固定延迟”的语义,而不是“固定间隔”。