websocket 有限制吗?
我是 websockets 的新手,并尝试在函数完成生成考试时使用 websockets 向用户推送通知。
工作原理:
- 用户按下按钮选择考试科目。
- 前端发送随机生成的用户名(uuid)并使用用户名连接到 websocket 服务器
- 连接成功后,订阅
/user/queue/finished-generate-something
并显示“生成考试”按钮。 - 用户,选择科目后,按“生成考试”并向“/generate-exam”发出请求,在此请求的正文中发送上面生成的用户名
- 完成生成考试后,将显示通知,并转到下一页
我不知道是否可以是正确的方法,但据说如果考试完成生成则进行 ping 操作对客户端不利。
这段代码可以在我的本地服务器上运行,也可以在生产服务器上运行,但是当大约 5000 个用户连接到 websocket 时,它停止发送消息(订阅频道和发送消息有效,并且在后端看到日志)
这是我的后端代码(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);
}
}
对于前端(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
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:
- User pressing button to choose subjects of exam.
- Frontend sending randomly generated username (uuid) and connecting to websocket server using username
- After success connection, subscribing to
/user/queue/finished-generating-something
and showing button "Generate exam". - User, after choosing subjects, presses generate exam and making request to "/generate-exam" sending username generated above in body of this request
- 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
总是有限制的。尝试在本地创建 http(s) 服务,然后打开与其的 5k 连接。它可能会失败。
由于这些限制,我在使用 websocket 时遇到了麻烦,很难判断您的服务器将能够处理多少个连接,而这些连接的“权重”取决于您的应用程序和您拥有的资源。
您至少会达到文件描述符限制。你会碰到CPU和内存限制。您将达到网络限制。设备之间的任何连接都可能会出现问题。
如果限制始终为 5k,听起来就像是人为设置的限制。如果连接开始随机下降,则很难判断。
现在,当我需要 websocket 时,我对 websocket 感到头疼,我什至不尝试运行经典虚拟机,而是更好地使用一些云服务,例如 AWS ApiGateway for Websocket API。如果你运行一些业余爱好项目,当然,socket.io 会做得很好或者任何替代方案。然而,一旦你开始达到这样的限制,它很容易就会成为一个相当大的挑战,至少从我个人在 ws 和 wss 上运行至少 5 个不同应用程序的经验来看是这样。
从文件描述符限制调试开始,更多信息如下:
https://unix .stackexchange.com/questions/84227/limits-on-the-number-of-file-descriptors
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