如何响应请求通过Spring在响应式Websocket上推送数据?

时间:2019-03-06 10:18:14

标签: java websocket spring-webflux project-reactor

我正在使用Spring Boot 2.1.3开始使用反应式Websocket。我创建了一个WebSocketHandler实现,如下所示:

@Override
public Mono<Void> handle(WebSocketSession session) {

Flux<EfficiencyData> flux = service.subscribeToEfficiencyData(1);
    var publisher = flux.map( o -> {
        try {
            return objectMapper.writeValueAsString(o);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }).map(session::textMessage)
      .delayElements(Duration.ofSeconds(1));
    return session.send(publisher);
}

这有效,如果我连接,则每秒在我的websocket客户端中序列化EfficiencyData

但是,我想响应来自websocket的请求,告诉service我想要数据的ID。我设法获得了这样的请求信息:

@Override
public Mono<Void> handle(WebSocketSession session) {

    return session.send(session.receive().map(webSocketMessage -> {
        int id = Integer.parseInt(webSocketMessage.getPayloadAsText());

        return session.textMessage("Subscribing with id " + id);
    }));

现在我不知道如何结合这两个实现?

我希望做这样的事情:

@Override
public Mono<Void> handle(WebSocketSession session) {

    return session.send(session.receive().map(webSocketMessage -> {
        int id = Integer.parseInt(webSocketMessage.getPayloadAsText());

        Flux<EfficiencyData> flux = service.subscribeToEfficiencyData(id);
        var publisher = flux.map( o -> {
            try {
                return objectMapper.writeValueAsString(o);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                return null;
            }
        }).map(session::textMessage)
                            .delayElements(Duration.ofSeconds(1));
        return publisher; //Does not compile
    }));

但是,由于publisherFlux<WebSocketMessage>,并且应该是Publisher<WebSocketMessage>,因此无法编译。应该如何处理?

编辑:

WebSocketHandler的Javadoc示例之后,我尝试了以下方法:

@Override
public Mono<Void> handle(WebSocketSession session) {
    Flux<EfficiencyData> flux =
            session.receive()
                   .map(webSocketMessage -> Integer.parseInt(webSocketMessage.getPayloadAsText()))
                   .concatMap(service::subscribeToEfficiencyData);
    Mono<Void> input = flux.then();
    Mono<Void> output = session.send(flux.map(data -> session.textMessage(data.toString()))).then();
    return Mono.zip(input, output).then();
}

但这只是立即断开websocket客户端的连接,而无需执行任何操作。

1 个答案:

答案 0 :(得分:0)

使用flatMapconcatMap来平整返回的发布者

要解决您的问题,您必须使用允许平整返回值的运算符。例如

@Override
public Mono<Void> handle(WebSocketSession session) {

    return session.send(
       session.receive()
              .flatMap(webSocketMessage -> {
                  int id = Integer.parseInt(webSocketMessage.getPayloadAsText());

                  Flux<EfficiencyData> flux = service.subscribeToEfficiencyData(id);
                  var publisher = flux
                      .<String>handle((o, sink) -> {
                         try {
                            sink.next(objectMapper.writeValueAsString(o));
                         } catch (JsonProcessingException e) {
                            e.printStackTrace();
                            return; // null is prohibited in reactive-streams
                         }
                      })
                      .map(session::textMessage)
                      .delayElements(Duration.ofSeconds(1));

                  return publisher;
              })
    );
}

要点

  1. 如果返回类型是流,请使用flatMapconcatMap(请参见区别here
  2. 从不返回Null。在反应流中,Null是禁止的值(请参阅规范规则here
  3. 当映射可以以null结尾时->使用handle运算符。查看更多说明here