简体   繁体   English

Spring webflux websocket - 多房间聊天

[英]Spring webflux websocket - Multiple rooms chat

I am currently building a chat application with spring reactive.我目前正在使用 spring 反应构建一个聊天应用程序。

Each user can be part of multiple chat rooms.每个用户都可以是多个聊天室的一部分。

I am trying to only send each chat message to clients connected to the corresponding rooms.我试图只将每条聊天消息发送给连接到相应房间的客户端。

For example, if a user sends a message in the chat room with UUID "32e4adff-d0bd-4496-ae40-0e799eeb5fe7", only clients connected to the socket with endpoint "/chat/32e4adff-d0bd-4496-ae40-0e799eeb5fe7" will receive the message.例如,如果用户在聊天室中发送一条 UUID 为“32e4adff-d0bd-4496-ae40-0e799eeb5fe7”的消息,则只有连接到端点为“/chat/32e4adff-d0bd-4496-ae40-0e799eeb5fe7”的套接字的客户端才会接收消息。

Currently, my websocket handler mapping is :目前,我的 websocket 处理程序映射是:

  @Bean
  public HandlerMapping webSocketHandlerMapping() {
    String path = "/chat/*";
    Map<String, WebSocketHandler> map = Map.of(path, webSocketHandler);
    return new SimpleUrlHandlerMapping(map, -1);
  } 

And my ChatSocketHandler :还有我的 ChatSocketHandler :

@Component
public class ChatSocketHandler implements WebSocketHandler {

  private final ObjectMapper mapper = new ObjectMapper();

  private Sinks.Many<ChatMessage> sinks = Sinks.many().multicast().directBestEffort();
  private Flux<ChatMessage> flux = sinks.asFlux();

  private final Sinks.EmitFailureHandler emitFailureHandler =
      (signalType, emitResult) -> emitResult.equals(Sinks.EmitResult.FAIL_NON_SERIALIZED);

  private final ChatService chatService;

  @Autowired
  public ChatSocketHandler(ChatService chatService) {
    this.chatService = chatService;
  }

  @Override
  public Mono<Void> handle(WebSocketSession session) {
    // TODO get rid of subscribe
    session
        .receive()
        .map(
            webSocketMessage -> {
              try {
                return mapper.readValue(webSocketMessage.getPayloadAsText(), ChatMessage.class);
              } catch (JsonProcessingException e) {
                e.printStackTrace();
                return new ChatMessage();
              }
            })
        .flatMap(chatService::sendMessage)
        .subscribe(webSocketMessage -> sinks.emitNext(webSocketMessage, emitFailureHandler));

    return session.send(
        Mono.delay(Duration.ofMillis(100))
            .thenMany(flux.map(it -> session.textMessage(toJson(it)))));
  }

  private String toJson(ChatMessage object) {
    try {
      return mapper.writeValueAsString(object);
    } catch (JsonProcessingException e) {
      e.printStackTrace();
    }
    return null;
  }
}

And chat service function to send a message :和聊天服务功能发送消息:

public Mono<ChatMessage> sendMessage(ChatMessage chatMessage) {
    return chatMessageRepository.insert(chatMessage);
}

For now, clients receive messages for all rooms, event the one they are not supposed to be connected to.目前,客户端会收到所有房间的消息,即使是他们不应该连接的房间。

How to configure my websocket so that only users connected to a room receive the messages published there ?如何配置我的 websocket 以便只有连接到房间的用户才能收到那里发布的消息?

I fixed it by adding a filter in my session.send我通过在 session.send 中添加过滤器来修复它

  @Override
  public Mono<Void> handle(WebSocketSession session) {
    String path = session.getHandshakeInfo().getUri().getPath();
    String chatId = path.substring(path.lastIndexOf('/') + 1);

    session
        .receive()
        .map(
            webSocketMessage -> {
              try {
                return mapper.readValue(webSocketMessage.getPayloadAsText(), ChatMessage.class);
              } catch (JsonProcessingException e) {
                e.printStackTrace();
                return new ChatMessage();
              }
            })
        .flatMap(chatService::sendMessage)
        .subscribe(webSocketMessage -> sinks.emitNext(webSocketMessage, emitFailureHandler));

    return session.send(
        Mono.delay(Duration.ofMillis(100))
            .thenMany(
                flux.filter(it -> chatId.equals(it.getChatId()))
                    .map(it -> session.textMessage(toJson(it)))));
  }

I am not sure whether there is a better way to do this.我不确定是否有更好的方法来做到这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM