perf(ext/websocket): replace tokio_tungstenite server with fastwebsockets (#18587)

https://github.com/littledivy/fastwebsockets

```
# This PR
./load_test 100 0.0.0.0 8080 0 0
Running benchmark now...
Msg/sec: 176355.000000

# main
./load_test 100 0.0.0.0 8080 0 0
Running benchmark now...
Msg/sec: 157198.750000
```
This commit is contained in:
Divy Srivastava 2023-04-06 18:44:31 +05:30 committed by GitHub
parent 3b62a58818
commit 4cc8784f5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 266 additions and 39 deletions

View file

@ -78,6 +78,11 @@ webidl.converters["WebSocketSend"] = (V, opts) => {
return webidl.converters["USVString"](V, opts);
};
/** role */
const SERVER = 0;
const CLIENT = 1;
/** state */
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
@ -86,6 +91,7 @@ const CLOSED = 3;
const _readyState = Symbol("[[readyState]]");
const _url = Symbol("[[url]]");
const _rid = Symbol("[[rid]]");
const _role = Symbol("[[role]]");
const _extensions = Symbol("[[extensions]]");
const _protocol = Symbol("[[protocol]]");
const _binaryType = Symbol("[[binaryType]]");
@ -98,6 +104,7 @@ const _idleTimeoutTimeout = Symbol("[[idleTimeoutTimeout]]");
const _serverHandleIdleTimeout = Symbol("[[serverHandleIdleTimeout]]");
class WebSocket extends EventTarget {
[_rid];
[_role];
[_readyState] = CONNECTING;
get readyState() {
@ -203,6 +210,7 @@ class WebSocket extends EventTarget {
}
this[_url] = wsURL.href;
this[_role] = CLIENT;
ops.op_ws_check_permission_and_cancel_handle(
"WebSocket.abort()",
@ -312,7 +320,13 @@ class WebSocket extends EventTarget {
const sendTypedArray = (view, byteLength) => {
this[_bufferedAmount] += byteLength;
PromisePrototypeThen(
core.opAsync2("op_ws_send_binary", this[_rid], view),
core.opAsync2(
this[_role] === SERVER
? "op_server_ws_send_binary"
: "op_ws_send_binary",
this[_rid],
view,
),
() => {
this[_bufferedAmount] -= byteLength;
},
@ -346,7 +360,11 @@ class WebSocket extends EventTarget {
const d = core.encode(string);
this[_bufferedAmount] += TypedArrayPrototypeGetByteLength(d);
PromisePrototypeThen(
core.opAsync2("op_ws_send_text", this[_rid], string),
core.opAsync2(
this[_role] === SERVER ? "op_server_ws_send_text" : "op_ws_send_text",
this[_rid],
string,
),
() => {
this[_bufferedAmount] -= TypedArrayPrototypeGetByteLength(d);
},
@ -401,7 +419,12 @@ class WebSocket extends EventTarget {
this[_readyState] = CLOSING;
PromisePrototypeCatch(
core.opAsync("op_ws_close", this[_rid], code, reason),
core.opAsync(
this[_role] === SERVER ? "op_server_ws_close" : "op_ws_close",
this[_rid],
code,
reason,
),
(err) => {
this[_readyState] = CLOSED;
@ -422,7 +445,7 @@ class WebSocket extends EventTarget {
async [_eventLoop]() {
while (this[_readyState] !== CLOSED) {
const { 0: kind, 1: value } = await core.opAsync2(
"op_ws_next_event",
this[_role] === SERVER ? "op_server_ws_next_event" : "op_ws_next_event",
this[_rid],
);
@ -489,7 +512,7 @@ class WebSocket extends EventTarget {
if (prevState === OPEN) {
try {
await core.opAsync(
"op_ws_close",
this[_role] === SERVER ? "op_server_ws_close" : "op_ws_close",
this[_rid],
code,
value,
@ -517,14 +540,23 @@ class WebSocket extends EventTarget {
clearTimeout(this[_idleTimeoutTimeout]);
this[_idleTimeoutTimeout] = setTimeout(async () => {
if (this[_readyState] === OPEN) {
await core.opAsync("op_ws_send", this[_rid], {
kind: "ping",
});
await core.opAsync(
this[_role] === SERVER ? "op_server_ws_send" : "op_ws_send",
this[_rid],
{
kind: "ping",
},
);
this[_idleTimeoutTimeout] = setTimeout(async () => {
if (this[_readyState] === OPEN) {
this[_readyState] = CLOSING;
const reason = "No response from ping frame.";
await core.opAsync("op_ws_close", this[_rid], 1001, reason);
await core.opAsync(
this[_role] === SERVER ? "op_server_ws_close" : "op_ws_close",
this[_rid],
1001,
reason,
);
this[_readyState] = CLOSED;
const errEvent = new ErrorEvent("error", {
@ -594,7 +626,9 @@ export {
_protocol,
_readyState,
_rid,
_role,
_server,
_serverHandleIdleTimeout,
SERVER,
WebSocket,
};