使用RxJava将无限多的分组事件流写入旋转文件

使用RxJava将无限多的分组事件流写入旋转文件,java,rxjs,rx-java,reactive-programming,rx-java2,Java,Rxjs,Rx Java,Reactive Programming,Rx Java2,我试图实现以下行为: 定期轮询/生成事件流(短时间,比如1秒) 然后根据某些内在特征对事件进行分组 每组事件都会立即写入匹配的文件(这是我想要维护的行为的关键) 在后续事件中,在密封/旋转文件之前,文件将被重新用于匹配组(具有相同的密钥) 在较长的持续时间(例如5秒)后,文件将被密封/旋转,并使用其他订阅者进行操作 我编写了以下示例代码以实现上述行为: 私有静态最终整数事件=3; 专用静态最终长较短=1L; 专用静态最终长=5L; 专用静态最终长睡眠=100000L; 公共静态void m

我试图实现以下行为:

  • 定期轮询/生成事件流(短时间,比如1秒)
  • 然后根据某些内在特征对事件进行分组
  • 每组事件都会立即写入匹配的文件(这是我想要维护的行为的关键)
  • 在后续事件中,在密封/旋转文件之前,文件将被重新用于匹配组(具有相同的密钥)
  • 在较长的持续时间(例如5秒)后,文件将被密封/旋转,并使用其他订阅者进行操作
我编写了以下示例代码以实现上述行为:


私有静态最终整数事件=3;
专用静态最终长较短=1L;
专用静态最终长=5L;
专用静态最终长睡眠=100000L;
公共静态void main(最终字符串[]args)引发异常{
val files=new DualHashBidiMap();
可观察的(事件)
.flatMap(num->Observable.fromIterable(ThreadLocalRandom.current().ints(num.boxed().collect(Collectors.toList()))
.groupBy(num->Math.abs(num%2))
.repeatWhen(完成->完成.延迟(更短,时间单位.秒))
.map(组->{
val file=files.computeIfAbsent(group.getKey(),Unchecked.function(key->file.createTempFile(String.format(“%03d-”,key),“.txt”));
subscribe(line->FileUtils.writeLines(file,StandardCharsets.UTF_8.name(),line,true));
返回文件;
})
.缓冲区(更长,时间单位为秒)
.flatMap(可观察::fromIterable)
.distinct(文件::getName)
.doOnNext(文件::removeValue)
.doOnNext(文件->系统.out.println(“文件-”+file+“,行-”+FileUtils.readLines(文件,StandardCharsets.UTF_8)))
.subscribe();
睡眠;
}
虽然它可以按预期工作(暂时将映射访问的线程安全问题放在一边,我使用的是
commons-collections4
中的bidi映射,只是为了演示),但我想知道是否有一种方法可以在不依赖外部映射访问的情况下以RX形式实现上述功能

请注意,在创建组时立即写入文件是至关重要的,这意味着我们必须使文件超出生成的事件组的范围


提前谢谢。

有意思的问题。。我可能错了,但我认为你无法避免在管道中的某个地方有一个
映射
文件

我认为我的解决方案可以进一步清理,但它似乎可以实现以下目标:

  • 消除了双向映射的需要
  • 无需调用
    Map.remove(…)
我建议您将编写的
文件的
映射
视为一个独特的
可观察的
,以较慢的间隔发出全新的
映射

    Observable<HashMap<Integer, File>> fileObservable = Observable.fromCallable(
                () -> new HashMap<Integer, File>() )
            .repeatWhen( completed -> completed.delay( LONGER, TimeUnit.SECONDS ));
到目前为止,很好,您已经获得了与活动一起提供的最新一组
文件。接下来,您必须处理
文件
。我想您可以使用
distinctUntilChanged()
来实现这一点。它应该非常有效,因为它将在封面下调用
HashMap.equals(…)
,并且
Map
对象的标识在大多数时间都不会改变
HashMap.equals(…)
首先检查相同的标识

由于此时您真正感兴趣的是处理前一组发出的
文件,而不是当前文件,因此可以使用
.scan((prev,current)->{…})操作符。下面是上面完整的代码块:

    Observable.just( EVENTS )
        .flatMap( num -> Observable.fromIterable(
                ThreadLocalRandom.current().ints( num ).boxed().collect( Collectors.toList() )))
        .groupBy( num -> Math.abs( num % 2 ))
        .repeatWhen( completed -> completed.delay( SHORTER, TimeUnit.SECONDS ))
        .withLatestFrom( fileObservable, ( group, files ) -> {

            File file = files.computeIfAbsent(
                    group.getKey(),
                    Unchecked.function( key -> File.createTempFile( String.format( "%03d-", key ), ".txt" )));

            group.map( Object::toString ).toList()
                .subscribe( lines -> FileUtils.writeLines(file, StandardCharsets.UTF_8.name(), lines, true ));

            return files;
        } )
        .distinctUntilChanged()
        .scan(( prev, current ) -> {

            Observable.fromIterable( prev.entrySet() )
                .map( Entry::getValue )
                .subscribe( file -> System.out.println( "File - '" + file + "', Lines - " +
                                FileUtils.readLines( file, StandardCharsets.UTF_8 )));

            return current;
        } )
        .subscribe();

    Thread.sleep( SLEEP );

比原来的解决方案稍微长一点,但可能会解决几个问题。

将地图本身作为可观察对象,并与最新版本结合使用是一个好主意:)听起来很有希望。我将在实际应用程序中测试它的有效性(当然,上面的代码只是一个示例),如果它有效,我将批准您的答案。谢谢你的努力!好的,这似乎是一个有效的方法。一些注释:1)
distinctUntilChanged
效果很好,因为映射相等首先测试对象级标识(
=
),该标识始终为真,然后进行内容比较,该比较始终为假,因为值是动态生成的临时文件。2) 我使用buffer(2,1).map(maps->Observable.fromIterable(maps.iterator().next().values())将可观测值转换为文件的可观测值,而不是扫描。再次感谢你的帮助
    Observable.just( EVENTS )
        .flatMap( num -> Observable.fromIterable(
                ThreadLocalRandom.current().ints( num ).boxed().collect( Collectors.toList() )))
        .groupBy( num -> Math.abs( num % 2 ))
        .repeatWhen( completed -> completed.delay( SHORTER, TimeUnit.SECONDS ))
        .withLatestFrom( fileObservable, ( group, files ) -> {

            File file = files.computeIfAbsent(
                    group.getKey(),
                    Unchecked.function( key -> File.createTempFile( String.format( "%03d-", key ), ".txt" )));

            group.map( Object::toString ).toList()
                .subscribe( lines -> FileUtils.writeLines(file, StandardCharsets.UTF_8.name(), lines, true ));

            return files;
        } )
        .distinctUntilChanged()
        .scan(( prev, current ) -> {

            Observable.fromIterable( prev.entrySet() )
                .map( Entry::getValue )
                .subscribe( file -> System.out.println( "File - '" + file + "', Lines - " +
                                FileUtils.readLines( file, StandardCharsets.UTF_8 )));

            return current;
        } )
        .subscribe();

    Thread.sleep( SLEEP );