使用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 );