Obito Uchiha
Obito Uchiha

Reputation: 45

Is there limit in websockets?

I'm new to websockets and tried to use websockets for pushing notification to user, when function is finished generating exam.
How it is working:

  1. User pressing button to choose subjects of exam.
  2. Frontend sending randomly generated username (uuid) and connecting to websocket server using username
  3. After success connection, subscribing to /user/queue/finished-generating-something and showing button "Generate exam".
  4. User, after choosing subjects, presses generate exam and making request to "/generate-exam" sending username generated above in body of this request
  5. After finishing generating exam, notification will be shown, and going to next page

I don't know if it is correct way to do it, but it was said pinging if exam is finished generating is bad for client.

This code is working on my local server and also worked on production server, but when ~5000 users connected to websocket, it stopped sending messages(subscription to channel and sending message worked, and logs was seen in backend)

Here is my code for backend(Spring):

WebSocketConfig:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOrigins("*")
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app");
        registry.enableSimpleBroker("/topic", "/user/queue/finished-generating-exam",
                "/user/queue/finished-checking-exam");
        registry.setUserDestinationPrefix("/user");
    }

//  used to add username for each connection
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
                StompHeaderAccessor accessor =
                        MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
                if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                    List<String> authorization = accessor.getNativeHeader("username");
                    accessor.setUser(new StompPrincipal(authorization.get(0)));
                }

                return message;
            }
        });
    }
}

ExamService

    @Async
    public ExamResponse<?> generateExam(String username) {

        //Generating exam

        webSocketService.sendFinishGenerationExam(username, "created");
    }

WebSocketService

@Slf4j
@Service
public class WebSocketService {

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    public void sendFinishGenerationExam(String username, String message) {
        simpMessagingTemplate.convertAndSendToUser(username,
                "/user/queue/finished-generating-exam", message);
        log.info("socket message sent " + message);
    }

    public void sendCheckingOfExamFinished(String username, String message) {
        simpMessagingTemplate.convertAndSendToUser(username,
                "/user/queue/finished-checking-exam", message);
        log.info("socket message sent " + message);
    }
}

And for frontend (React):

import SockJS from "sockjs-client";
import { Client } from "@stomp/stompjs";
//some code
const client = new Client({
        webSocketFactory: function () {
          return new SockJS(
            REACT_APP_WEBSOCKET_URL
          );
        },
        connectHeaders: {
          username: username,
        },
        reconnectDelay: 5000,
        debug: function (str) {
          console.log(str);
        },
      });

      client.onConnect = function (frame) {

        let sessionId = getSessionIdFromUrl(client.webSocket._transport.url);

        client.subscribe(
          "/user/queue/finished-generating-exam" + "-user" + sessionId,
          onMessageReceived
        );
      };

      client.activate();
// some code

Upvotes: 2

Views: 4982

Answers (1)

Lukas Liesis
Lukas Liesis

Reputation: 26393

There are always limits. Try creating http(s) service locally and then open 5k connections to it. It will probably fail.

I had bad times with websockets because of these limits, it's a bit hard to tell how many connections your server will be able to handle, while "weight" of those connections depends on your app and resources you have.

You will hit file descriptor limit at minimum. You will hit CPU & RAM limits. You will hit your network limits. There are all kind of things that can go wrong with any connection between devices.

If limit is always at 5k, it sounds like a set artificial limit. If connections start to drop at pretty much random, it's pretty hard to tell.

From all of the headaches i had about websockets now when i need websocket, i don't even try to run classic VM, but better use some cloud service like AWS ApiGateway for Websocket API. If you run some hobby project, sure, socket.io will do perfectly fine or any alternative. Yet once you start to hit such limits it can easily become quite a challenge, at least that's from my personal experience running at least 5 different applications over ws and wss.

Start with file descriptor limit debug, more info here:

https://unix.stackexchange.com/questions/84227/limits-on-the-number-of-file-descriptors

Upvotes: 4

Related Questions