Java 如何不依赖于反应器中的订阅时间

Java 如何不依赖于反应器中的订阅时间,java,reactive-programming,project-reactor,Java,Reactive Programming,Project Reactor,我已经阅读了整个反应堆的文档,但是我无法找到以下问题的正确模式。 我有一个方法,应该异步地做一些事情。我以流量的形式返回结果响应,消费者可以订阅它 该方法具有以下定义: Flux<ResultMessage> sendRequest(RequestMessage message); 实现可以如下所示: Flux<ResultMessage> sendRequest(RequestMessage message) { Flux<ResultMessage>

我已经阅读了整个反应堆的文档,但是我无法找到以下问题的正确模式。 我有一个方法,应该异步地做一些事情。我以流量的形式返回结果响应,消费者可以订阅它

该方法具有以下定义:

Flux<ResultMessage> sendRequest(RequestMessage message);
实现可以如下所示:

Flux<ResultMessage> sendRequest(RequestMessage message) {
   Flux<ResultMessage> result = incomingMessageStream
            .filter( resultMessage -> Objects.equals( resultMessage.getId(), message.getId() ) )
            .take( 2 );
// The message sending is done here...

    return result;
}
通量发送请求(请求消息){ 流量结果=输入消息流 .filter(resultMessage->Objects.equals(resultMessage.getId(),message.getId()) .采取(2); //消息发送在此处完成。。。 返回结果; } 其中,
incomingMessageStream
是通过此通道的所有消息的流量。 这个实现的问题是,消费者在结果消息发出后被订阅,它可能会错过其中一些消息


所以,我正在寻找一个解决方案,让消费者不依赖于订阅时间。潜在消费者可能根本不需要订阅产生的
Flux
。我正在寻找一个通用的解决方案,但如果不可能,您可以假定生成的消息数不超过2。

一段时间后,我创建了一个似乎有效的解决方案:

Flux<ResultMessage> sendRequest(RequestMessage message) {
  final int maxResponsesCount = 2;
  final Duration responseTimeout = Duration.ofSeconds( 10 );
  final Duration subscriptionTimeout = Duration.ofSeconds( 5 );

  // (1) 
  ConnectableFlux<ResultMessage> result = incomingMessageStream
      .ofType( ResultMessage.class )
      .filter( resultMessage ->Objects.equals(resultMessage.getId(), message.getId() ) )
      .take( maxResponsesCount )
      .timeout( responseTimeout )
      .replay( maxResponsesCount );
  Disposable connectionDisposable = result.connect();

  // (2)
  AtomicReference<Subscription> subscriptionForCancelSubscription = new AtomicReference<>();
  Mono.delay( subscriptionTimeout )
    .doOnSubscribe( subscriptionForCancelSubscription::set )
    .subscribe( x -> connectionDisposable.dispose() );

  // The message sending is done here...

  // (3)
  return result
    .doOnSubscribe(s ->subscriptionForCancelSubscription.get().cancel())
    .doFinally( signalType -> connectionDisposable.dispose() );
}
通量发送请求(请求消息){ 最终整数MaxResponseCount=2; 最终持续时间响应超时=持续时间秒(10); 最终持续时间subscriptionTimeout=持续时间秒(5); // (1) ConnectableFlux结果=incomingMessageStream .of类型(ResultMessage.class) .filter(resultMessage->Objects.equals(resultMessage.getId(),message.getId()) .take(最大响应计数) .超时(响应超时) .重播(最大响应计数); 一次性连接Disposable=result.connect(); // (2) AtomicReference subscriptionForCancelSubscription=新的AtomicReference(); 单声道延迟(subscriptionTimeout) .DoonSubscripte(订阅ForCancelSubscription::set) .subscribe(x->connectionDisposable.dispose()); //消息发送在此处完成。。。 // (3) 返回结果 .doonSubscripte(s->subscriptionForCancelSubscription.get().cancel()) .doFinally(signalType->connectionDisposable.dispose()); } 我使用的ConnectableFlux可以立即连接到流,而无需订阅,它被设置为使用reply()方法来存储所有消息,这样以后任何订阅方都不会错过响应消息(1)

可以执行的路径很少:

  • 方法,但尚未对通量执行订阅
    • 解决方案-如果未完成订阅,则有一个计时器可在5秒后删除连接的流量资源。(二)
  • 方法被调用并订阅该流量

    2.1。没有返回任何消息

    • 解决方案-设置了获取响应的超时(
      .timeout(responseTimeout)
      )。然后,
      .doFinally(…)
      清理资源(1)(3)
    2.2。已返回一些响应消息

    • 解决方案-与2.1相同
    2.3。已返回所有响应消息

    • 解决方案-执行
      doFinally()
      是因为达到了最大元素数(
      .take(maxresponsecount)
      )(1)(3)
  • 我还没有对这个问题进行认真的测试,如果出现问题,我会对这个答案进行修正

    Flux<ResultMessage> sendRequest(RequestMessage message) {
      final int maxResponsesCount = 2;
      final Duration responseTimeout = Duration.ofSeconds( 10 );
      final Duration subscriptionTimeout = Duration.ofSeconds( 5 );
    
      // (1) 
      ConnectableFlux<ResultMessage> result = incomingMessageStream
          .ofType( ResultMessage.class )
          .filter( resultMessage ->Objects.equals(resultMessage.getId(), message.getId() ) )
          .take( maxResponsesCount )
          .timeout( responseTimeout )
          .replay( maxResponsesCount );
      Disposable connectionDisposable = result.connect();
    
      // (2)
      AtomicReference<Subscription> subscriptionForCancelSubscription = new AtomicReference<>();
      Mono.delay( subscriptionTimeout )
        .doOnSubscribe( subscriptionForCancelSubscription::set )
        .subscribe( x -> connectionDisposable.dispose() );
    
      // The message sending is done here...
    
      // (3)
      return result
        .doOnSubscribe(s ->subscriptionForCancelSubscription.get().cancel())
        .doFinally( signalType -> connectionDisposable.dispose() );
    }