Java 如何让多个订阅服务器运行在不同的执行上下文/线程上

Java 如何让多个订阅服务器运行在不同的执行上下文/线程上,java,resttemplate,spring-webflux,project-reactor,spring-websocket,Java,Resttemplate,Spring Webflux,Project Reactor,Spring Websocket,我正在开发一个用于物联网实时数据可视化的Spring Boot WebFlux应用程序 我有一个Flux,它模拟来自设备的数据,我希望在每个事件建立websocket连接时: 必须通过websocket发送以进行实时可视化(使用反应式WebSocketHandler) 必须根据给定条件进行检查,以便通过HTTP REST调用发送通知(RestTemplate) 从我的日志来看,两个订阅者(websocket处理程序和通知程序)似乎获得了两个具有完全不同值的不同流(在日志下面) 我还在MySo

我正在开发一个用于物联网实时数据可视化的Spring Boot WebFlux应用程序

我有一个
Flux
,它模拟来自设备的数据,我希望在每个事件建立websocket连接时:

  • 必须通过websocket发送以进行实时可视化(使用反应式
    WebSocketHandler
  • 必须根据给定条件进行检查,以便通过HTTP REST调用发送通知(
    RestTemplate
从我的日志来看,两个订阅者(websocket处理程序和通知程序)似乎获得了两个具有完全不同值的不同流(在日志下面)

我还在
MySource
类中的
map
之后尝试了一个变量链接
share
方法,在这种情况下,看起来虽然我只有一个通量,但只有一个线程,所以一切都被阻塞了(我可以看到REST调用阻止了通过websocket发送)

这里发生了什么?如何使这两个订阅服务器在不同的执行上下文(不同的线程)中运行,使它们彼此完全独立

下面是相关的代码片段和日志

提前谢谢大家

更新:为了清晰起见,我必须指定
MyEvent
s具有随机生成的值,因此我通过使用
ConnectableFlux
/
共享
解决了一个问题,该共享保证具有相同的
Flux
,但我仍然希望为这两个订阅者提供单独的执行上下文

public class MyWebSocketHandler implements WebSocketHandler {

   @Autowired
   public MySource mySource;

   @Autowired
   public Notifier notifier;

   public Mono<Void> handle(WebSocketSession webSocketSession) {
            Flux<MyEvent> events = mySource.events();
            events.subscribe(event -> notifier.sendNotification(event));
            return webSocketSession.send(events.map(this::toJson).map(webSocketSession::textMessage));
   }

   private String toJson(MyEvent event) {
       log.info("websocket toJson " + event.getValue());
       ...
   }
}

这里有两个问题,第一个
restemplate
是同步/阻塞HTTP客户端,因此您应该使用
WebClient
,它是被动的,也可以创建
ConnectableFlux
Flux
,它可以有多个订阅者)您需要在
映射
操作符之前共享它,并创建新的
流量
-es,这些流量是从连接的流量创建的

例如:

Flux<MyEvent> connectedFlux = mySource.events().share();
Flux.from(connectedFlux).subscribe(event -> notifier.sendNotification(event));
return webSocketSession.send(Flux.from(connectedFlux).map(this::toJson).map(webSocketSession::textMessage));

现在看看执行上下文是否不同。

我设法使用了
ConnectableFlux
/
share
,因此现在保证了单个Flux源,但问题是执行websocket的线程调用了
sendNotification
。我想要实现的是为订阅者分离执行上下文。可能吗?我觉得我必须在某个地方添加
subscribeOn
,但我不确定……我已经编辑了我的问题主体,以便更好地澄清这一点。@vortex.alex是的,这是可能的,但是不能使用
subscribeOn
publishOn
,因为WebClient和被动WebSocket都有自己的非阻塞线程,您的
subscribeOn
publishOn
将被其内部执行定义覆盖。在我的答案中添加了更多的代码。我尝试了,但不是没有从
网络客户端得到任何响应,就是在
交换
检索
之后放置了
,但它不起作用,因为我有以下错误:
块()/blockFirst()/blockLast()正在阻止,thread parallel-2中不支持此功能。我是否应该在
WebClient
上明确订阅以触发请求(例如使用
block
)?或者,
exchange
返回的
Mono
必须通过
sendNotification
方法返回,但是接下来该怎么办呢?是否与您提到的
zip
方法有关?当然,您必须调用
subscribe
来启动响应式执行,以便
notifier.sendNotification(event.subscribe()
将启动执行。您不必对
Controller
s中返回的反应式方法/运算符链调用
subscribe
,因为Spring隐式调用它,但对于所有其他执行,您需要显式调用它。当您需要下游多个独立反应性方法执行的结果以进行进一步操作时,
zipWith
运算符可用于独立反应性执行。
public class Notifier {

   public void sendNotification (MyEvent event) {
      log.info("notifier sendNotification " + event.getValue());
      if (condition met)
         restTemplate.exchange(...)
   }
}
2019-11-19 11:58:55.375 INFO [     parallel-3] i.a.m.websocket.MyWebSocketHandler  : websocket toJson 4.09
2019-11-19 11:58:55.375 INFO [     parallel-1] i.a.m.notifier.Notifier : notifier sendNotification 4.86
2019-11-19 11:58:57.366 INFO [     parallel-1] i.a.m.notifier.Notifier : notifier sendNotification 4.24
2019-11-19 11:58:57.374 INFO [     parallel-3] i.a.m.websocket.MyWebSocketHandler  : websocket toJson 4.11
2019-11-19 11:58:59.365 INFO [     parallel-1] i.a.m.notifier.Notifier : notifier sendNotification 4.61
2019-11-19 11:58:59.374 INFO [     parallel-3] i.a.m.websocket.MyWebSocketHandler  : websocket toJson 4.03
2019-11-19 11:59:01.365 INFO [     parallel-1] i.a.m.notifier.Notifier : notifier sendNotification 4.88
2019-11-19 11:59:01.375 INFO [     parallel-3] i.a.m.websocket.MyWebSocketHandler  : websocket toJson 4.29
2019-11-19 11:59:03.364 INFO [     parallel-1] i.a.m.notifier.Notifier : notifier sendNotification 4.37
Flux<MyEvent> connectedFlux = mySource.events().share();
Flux.from(connectedFlux).subscribe(event -> notifier.sendNotification(event));
return webSocketSession.send(Flux.from(connectedFlux).map(this::toJson).map(webSocketSession::textMessage));
public class Notifier {

   public Mono<Void> sendNotification (MyEvent event) {
      log.info("notifier sendNotification " + event.getValue());
      return Mono.just(event)
                 .filter(e -> /* your condition */)
                 .flatMap(e -> WebClient.builder().baseUrl("XXX")...)
                 .then();
   }

}