Skip to content

Much faster WebSocket-http1 codec for Netty with minimal per-frame heap allocations

License

Notifications You must be signed in to change notification settings

jauntsdn/netty-websocket-http1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

98 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Maven Central Build

netty-websocket-http1

Alternative Netty implementation of RFC6455 - the WebSocket protocol.

Its advantages are significant per-core throughput improvement (1.8 - 2x) for small frames compared to netty's out-of-the-box websocket codecs, minimal heap allocations on frame path, and compatibility with netty-websocket-http2.

use case & scope

  • Intended for dense binary data & text messages: no extensions (compression) support.

  • No per-frame heap allocations in websocket frameFactory / decoder.

  • Library assumes small frames - many have payload <= 125 bytes, most are < 1500, maximum supported is 65k (65535 bytes).

  • Just codec - fragments, pings, close frames are decoded & protocol validated only. It is responsibility of user code to handle frames according to protocol (reassemble frame fragments, perform graceful close, respond to pings) and do utf8 validation of inbound text frames (utility is provided).

  • Single-threaded (transport IO event-loop) callbacks / frame factory API - in practice user code has its own message types to carry data, and external means (e.g. mpsc / spsc queues) may be used to properly publish messages on eventloop thread.

  • On encoder side 3 use cases are supported: frame factory [1] (create bytebuffer and encode frame prefix), frame encoder [2] (encode frame prefix into provided bytebuffer), frame bulk-encoder [3] (much more performant - encode multiple frames into provided bytebuffer).

performance

Per-core throughput this codec perf-test, netty built-in codec perf-test comparison with netty's out-of-the-box websocket handlers: non-masked frames with 8, 64, 125, 1000 bytes of payload over encrypted/non-encrypted connection.

java 9+

./gradlew clean build installDist
./perf_server_run.sh
./perf_client_run.sh
  • encrypted
payload size this codec, million messages netty's codec, million messages
8 2.8 1.45
64 2.3 1.2
125 1.9 1.1
1000 0.52 0.35

websocket-http2

Library may be combined with jauntsdn/websocket-http2 using http1 codec

for significantly improved per-core throughput this codec perf-test, netty built-in codec perf-test:

  • encrypted
payload size this codec, million msgs netty's codec, million msgs
8 0.93 0.56
125 0.74 0.464
1000 0.275 0.211

frameFactory / callbacks API

WebSocketFrameFactory to create outbound frames. It is library user responsibility to mask outbound frame once payload is written ByteBuf WebSocketFrameFactory.mask(ByteBuf)

public interface WebSocketFrameFactory {

  ByteBuf createBinaryFrame(ByteBufAllocator allocator, int binaryDataSize);
  
  // ByteBuf createTextFrame(ByteBufAllocator allocator, int binaryDataSize);
  
  // ByteBuf create*Fragment*(ByteBufAllocator allocator, int textDataSize);

  // create*Frame are omitted for control frames, created in similar fashion

  ByteBuf mask(ByteBuf frame);
}

WebSocketFrameListener to receive inbound frames

public interface WebSocketFrameListener {

  void onChannelRead(
      ChannelHandlerContext ctx, boolean finalFragment, int rsv, int opcode, ByteBuf payload);
   
  // netty handler callbacks are omitted for brevity

  // lifecycle
  default void onOpen(ChannelHandlerContext ctx) {}

  default void onClose(ChannelHandlerContext ctx) {}
}

WebSocketCallbacksHandler to exchange WebSocketFrameListener for WebSocketFrameFactory on successful websocket handshake

public interface WebSocketCallbacksHandler {

  WebSocketFrameListener exchange(
      ChannelHandlerContext ctx, WebSocketFrameFactory webSocketFrameFactory);
}

tests

  • WebSocket frames integration test: control & data frames of all allowed sizes, jauntsdn/netty-websocket-http1 client, netty websocket server.

  • WebSocket frames long-running soak test: exercising all logic paths of codec with 100m of randomized frames over multiple connections: netty websocket client, jauntsdn/netty-websocket-http1 server.

  • Perf test: per-core throughput of jauntsdn/netty-websocket-http1 client & server.

examples

netty-websocket-http1-perftest may serve as API showcase for both client and server:

build & binaries

./gradlew

Releases are published on MavenCentral

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.jauntsdn.netty:netty-websocket-http1:1.2.0"
}

LICENSE

Copyright 2022-Present Maksym Ostroverkhov.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.