RxJava-如何为长轮询创建服务器

RxJava-如何为长轮询创建服务器,java,reactive-programming,rx-java,Java,Reactive Programming,Rx Java,我有一个集群中有多个节点的应用程序。每个节点将日志文件写入其本地磁盘。我已经实现了一个日志搜索功能,可以在每个节点上搜索日志。从浏览器接收搜索请求的所有者节点将日志搜索作业提交给其他节点,然后其他节点将搜索结果传递给原始节点。客户端web浏览器使用长轮询从节点获取搜索结果。这似乎非常适合RxJava,因为每个节点都是一个事件流,客户端从所有节点获得一个整合的事件流。(假设吝啬的运营团队不允许我们使用Splunk或其他商业日志解决方案) 客户端在原始节点上轮询RESTAPI,该节点收集搜索结果。我

我有一个集群中有多个节点的应用程序。每个节点将日志文件写入其本地磁盘。我已经实现了一个日志搜索功能,可以在每个节点上搜索日志。从浏览器接收搜索请求的所有者节点将日志搜索作业提交给其他节点,然后其他节点将搜索结果传递给原始节点。客户端web浏览器使用长轮询从节点获取搜索结果。这似乎非常适合RxJava,因为每个节点都是一个事件流,客户端从所有节点获得一个整合的事件流。(假设吝啬的运营团队不允许我们使用Splunk或其他商业日志解决方案)

客户端在原始节点上轮询RESTAPI,该节点收集搜索结果。我对REST API的理想逻辑如下:

  • 服务器应使客户端最多等待15秒以获得响应
  • 如果在15秒内未生成任何结果,则响应可能为空。客户端将看到这一点并发送一个新的轮询请求
  • 如果没有更多结果(即搜索完成),请向客户端发送特殊响应,指示其不再轮询
  • 如果生成了结果,则最多再等待100毫秒,等待其他结果,以节省额外轮询的网络开销
  • 客户端不应获得任何重复的结果
我编写了以下示例代码来模拟此场景:

public static void main(String[] args) throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(5);

    /* Each searchTask represents the results of a search job running on a
     * node in the cluster */
    Subject<String,String> searchTask1 = PublishSubject.create();
    Subject<String,String> searchTask2 = PublishSubject.create();

    // Limit max number of search results
    Observable<String> searchResults = 
        Observable.merge(searchTask1, searchTask2).take( 1000 );

    /* Add a 100ms buffer window to collect nearby responses together.  
     * Filter out any empty buffers to eliminate unnecessary
     * responses to the browser. */
    BlockingObservable<List<String>> blocking = 
        searchResults.buffer(100, TimeUnit.MILLISECONDS)
            .filter(results -> !results.isEmpty()).toBlocking();
    Iterator<List<String>> it = blocking.getIterator();

    /* Each call to searchTask.onNext represents a search result pushed
     * to the owner node from another node.  This code would be called 
     * from the REST endpoint. */
    executorService.submit( () -> {
        searchTask1.onNext("1");
        try { Thread.sleep(1200); } catch ( Exception ignored ) { }
        searchTask1.onNext("2");
        searchTask1.onCompleted();
    });
    executorService.submit( () -> {
        searchTask2.onNext("a");
        try { Thread.sleep(500); } catch ( Exception ignored ) { }
        searchTask2.onNext("b");
        searchTask2.onCompleted();
    });

    executorService.submit( () -> {
        /* Each iteration of this loop represents a polling request from
         * the browser and the results that are sent back to it. */
        for ( int i = 0; i < 5; i++ ) { 
            it.forEachRemaining(results -> System.out.println(results));
        }
    });

    Thread.sleep(1500);
    System.out.println("exit");
}
publicstaticvoidmain(字符串[]args)引发异常{
ExecutorService ExecutorService=Executors.newFixedThreadPool(5);
/*每个searchTask表示在服务器上运行的搜索作业的结果
*群集中的节点*/
Subject searchTask1=PublishSubject.create();
Subject searchTask2=PublishSubject.create();
//限制搜索结果的最大数量
可观察的搜索结果=
可观察。合并(searchTask1,searchTask2)。获取(1000);
/*添加一个100ms的缓冲窗口以收集附近的响应。
*过滤掉所有空缓冲区以消除不必要的
*对浏览器的响应*/
阻塞可观测阻塞=
searchResults.buffer(100,时间单位为毫秒)
.filter(results->!results.isEmpty()).toBlocking();
Iterator it=blocking.getIterator();
/*每次调用searchTask.onNext都代表一个搜索结果
*从另一个节点发送到所有者节点。将调用此代码
*从REST端点开始*/
executorService.submit(()->{
searchTask1.onNext(“1”);
尝试{Thread.sleep(1200);}catch(异常被忽略){}
searchTask1.onNext(“2”);
searchTask1.onCompleted();
});
executorService.submit(()->{
searchTask2.onNext(“a”);
尝试{Thread.sleep(500);}catch(异常被忽略){}
searchTask2.onNext(“b”);
searchTask2.onCompleted();
});
executorService.submit(()->{
/*此循环的每次迭代表示来自的轮询请求
*浏览器及其返回的结果*/
对于(int i=0;i<5;i++){
it.forEachRemaining(结果->系统输出.println(结果));
}
});
睡眠(1500);
System.out.println(“退出”);
}
for循环中的逻辑应该是什么,以确保响应总是在最多15秒后发送回客户端(即使响应为空)


编辑:我已经用更多注释更新了示例代码,并显示了我当前的解决方案,但我仍然无法获得我正在寻找的15秒的最大响应时间。我们的网络设备会关闭闲置时间过长的HTTP连接,因此我想保证客户端在最多15秒后总能收到响应。

我能找到的所有RxJava文章似乎都非常关注客户端代码,而不是服务器端。然而,在与可观测算子争论了一段时间后,我提出了以下解决方案,接近我想要的。心跳每15秒发生一次,而不是上一次结果后的15秒。这意味着服务器可能会在发送结果后立即向客户端发送心跳响应,但这对我来说已经足够接近了

我创建了一个15秒的可观察间隔,并将其与我已有的
搜索结果
可观察间隔合并。我使用了一个主题,这样我可以在结果流停止时停止可观察的间隔(否则它将无限期地继续)

/*添加一个心跳信号,以确保我们不会在两次心跳之间等待太久
*发送响应和某些网络设备会终止我们的连接*/
最终可观测心跳=
可观察。间隔(1,时间单位。秒)。地图(el->“心跳”);
final PublishSubject stopHeartbeat=PublishSubject.create();
subscribe(el->{},ex->{},()->stopHeartbeat.onNext(null));
最终可观察的搜索结果Withheartbeat=
searchResults.mergeWith(heartbeat.takeUntil(stopHeartbeat));

您能详细说明一下吗,比如在收到部分结果时如何轮询进一步的结果,以及300毫秒的超时时间是多少?@zsxwing我已经用更多的细节更新了示例代码。这不是对您问题的回答,但您看过了吗?
    /* Add a heartbeat that ensures we don't wait too long between
     * sending responses and some network device kills our connection */
    final Observable<String> heartbeat =
        Observable.interval( 1, TimeUnit.SECONDS ).map(el -> "heartbeat");
    final PublishSubject<String> stopHeartbeat = PublishSubject.create();
    searchResults.subscribe( el -> {}, ex -> {}, () -> stopHeartbeat.onNext( null ) );
    final Observable<String> searchResultsWithHeartbeat =
        searchResults.mergeWith( heartbeat.takeUntil( stopHeartbeat ) );