Skip to content

Commit

Permalink
Merge branch 'release/1.2.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
mostroverkhov committed Jan 3, 2023
2 parents 57a945b + 057f9c0 commit 4e37a0e
Show file tree
Hide file tree
Showing 27 changed files with 946 additions and 100 deletions.
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ It is websockets-over-http2 support with no http1 dependencies and minimal overh

[https://jauntsdn.com/post/netty-websocket-http2/](https://jauntsdn.com/post/netty-websocket-http2/)

### much faster http1 codec
Integration with [jauntsdn/netty-websocket-http1](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-callbacks-codec) codec for websocket-http1
frames processing [improves](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/perftest/callbackscodec)
throughput 1.4x - 1.7x for small messages.

### websocket channel API
Intended for application servers and clients.
Allows transparent application of existing http1 websocket handlers on top of http2 stream.
Expand Down Expand Up @@ -82,7 +87,7 @@ EchoWebSocketHandler http1WebSocketHandler = new EchoWebSocketHandler();
Http2WebSocketClientHandshaker handShaker = Http2WebSocketClientHandshaker.create(channel);
Http2Headers headers =
new DefaultHttp2Headers().set("user-agent", "jauntsdn-websocket-http2-client/1.1.6");
new DefaultHttp2Headers().set("user-agent", "jauntsdn-websocket-http2-client/1.2.0");
ChannelFuture handshakeFuture =
/*http1 websocket handler*/
handShaker.handshake("/echo", headers, new EchoWebSocketHandler());
Expand Down Expand Up @@ -220,15 +225,26 @@ Library relies on capabilities provided by netty's `Http2ConnectionHandler` so p
[netty-websocket-http2-perftest](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/perftest)
module contains application that gives rough throughput/latency estimate. The application is started with `perf_server.sh`, `perf_client.sh`.

On modern box one can expect following results for single websocket:
On modern box one can expect following results for single websocket, 140 bytes payload (TLS connection, per-core throughput):

```properties
19:31:58.537 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.client.Main p50 => 435 micros
19:31:58.537 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.client.Main p95 => 662 micros
19:31:58.537 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.client.Main p99 => 841 micros
19:31:58.537 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.client.Main throughput => 205874 messages
19:31:58.537 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.client.Main throughput => 201048.83 kbytes
15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main p50 => 639 micros
15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main p95 => 947 micros
15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main p99 => 1110 micros
15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main throughput => 505140 messages
15:15:23.978 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main throughput => 69062.11 kbytes
```

Integration with [jauntsdn/netty-websocket-http1](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-callbacks-codec) codec for websocket-http1 frames
processing significantly [improves](https://github.com/jauntsdn/netty-websocket-http2/tree/develop/netty-websocket-http2-perftest/src/main/java/com/jauntsdn/netty/handler/codec/http2/websocketx/perftest/callbackscodec)
throughput:

```properties
15:13:10.483 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main p50 => 492 micros
15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main p95 => 702 micros
15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main p99 => 825 micros
15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main throughput => 746880 messages
15:13:10.484 epollEventLoopGroup-2-1 com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main throughput => 102112.5 kbytes
```

To evaluate performance with multiple connections we compose an application comprised with simple echo server, and client
Expand Down Expand Up @@ -270,7 +286,7 @@ repositories {
}
dependencies {
implementation 'com.jauntsdn.netty:netty-websocket-http2:1.1.6'
implementation 'com.jauntsdn.netty:netty-websocket-http2:1.2.0'
}
```

Expand Down
6 changes: 1 addition & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ subprojects {
mavenCentral()
}

dependencyLocking {
lockAllConfigurations()
}

plugins.withType(JavaPlugin) {

compileJava {
Expand Down Expand Up @@ -125,4 +121,4 @@ def projectVersion(project) {
return project.version + versionSuffix
}

defaultTasks "clean", "build"
defaultTasks "clean", "build", "installDist"
5 changes: 3 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
group=com.jauntsdn.netty
version=1.2.0
version=1.2.1

googleJavaFormatPluginVersion=0.9
dependencyManagementPluginVersion=1.1.0
gitPluginVersion=0.13.0
osDetectorPluginVersion=1.7.1
versionsPluginVersion=0.44.0

nettyVersion=4.1.85.Final
nettyVersion=4.1.86.Final
jauntNettyWebsocketHttp1=0.9.0
nettyTcnativeVersion=2.0.54.Final
hdrHistogramVersion=2.1.12
slf4jVersion=1.7.36
Expand Down
1 change: 1 addition & 0 deletions gradle/dependency-management.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ subprojects {
mavenBom "org.junit:junit-bom:${junitVersion}"
}
dependencies {
dependency "com.jauntsdn.netty:netty-websocket-http1:${jauntNettyWebsocketHttp1}"
dependency "org.hdrhistogram:HdrHistogram:${hdrHistogramVersion}"
dependency "io.netty:netty-tcnative-classes:${nettyTcnativeVersion}"
dependency "io.netty:netty-tcnative-boringssl-static:${nettyTcnativeVersion}"
Expand Down
2 changes: 1 addition & 1 deletion gradle/publishing.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,5 @@ def isRelease(Project project) {
}

def releaseModules() {
return "netty-websocket-http2"
return ["netty-websocket-http2", "netty-websocket-http2-callbacks-codec"]
}
38 changes: 38 additions & 0 deletions netty-websocket-http2-callbacks-codec/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2020 - 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.
*/

plugins {
id "java-library"
id "maven-publish"
id "signing"
}

description = "netty-websocket-http2 integration with jauntsdn/netty-websocket-http1: high-performance websocket codec"

dependencies {
api project(":netty-websocket-http2")
api "com.jauntsdn.netty:netty-websocket-http1"
}

dependencyLocking {
lockAllConfigurations()
}

tasks.named("jar") {
manifest {
attributes("Automatic-Module-Name": "com.jauntsdn.netty.websocket.http2.codec.callbacks")
}
}
21 changes: 21 additions & 0 deletions netty-websocket-http2-callbacks-codec/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.google.code.findbugs:jsr305:3.0.2=googleJavaFormat1.6
com.google.errorprone:error_prone_annotations:2.0.18=googleJavaFormat1.6
com.google.errorprone:javac-shaded:9+181-r4173-1=googleJavaFormat1.6
com.google.googlejavaformat:google-java-format:1.6=googleJavaFormat1.6
com.google.guava:guava:22.0=googleJavaFormat1.6
com.google.j2objc:j2objc-annotations:1.1=googleJavaFormat1.6
com.jauntsdn.netty:netty-websocket-http1:0.9.0=compileClasspath
io.netty:netty-buffer:4.1.86.Final=compileClasspath
io.netty:netty-codec-http2:4.1.86.Final=compileClasspath
io.netty:netty-codec-http:4.1.86.Final=compileClasspath
io.netty:netty-codec:4.1.86.Final=compileClasspath
io.netty:netty-common:4.1.86.Final=compileClasspath
io.netty:netty-handler:4.1.86.Final=compileClasspath
io.netty:netty-resolver:4.1.86.Final=compileClasspath
io.netty:netty-transport-native-unix-common:4.1.86.Final=compileClasspath
io.netty:netty-transport:4.1.86.Final=compileClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.14=googleJavaFormat1.6
empty=annotationProcessor
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.jauntsdn.netty.handler.codec.http2.websocketx;

import com.jauntsdn.netty.handler.codec.http.websocketx.WebSocketProtocol;
import io.netty.handler.codec.http.websocketx.WebSocketDecoderConfig;
import io.netty.handler.codec.http.websocketx.WebSocketFrameDecoder;
import io.netty.handler.codec.http.websocketx.WebSocketFrameEncoder;

/** Provides integration with jauntsdn/netty-websocket-http1 - high performance websocket codec. */
public final class WebSocketCallbacksCodec implements Http1WebSocketCodec {
private static final WebSocketCallbacksCodec INSTANCE = new WebSocketCallbacksCodec();

private WebSocketCallbacksCodec() {}

public static WebSocketCallbacksCodec instance() {
return INSTANCE;
}

@Override
public WebSocketFrameEncoder encoder(boolean maskPayload) {
return WebSocketProtocol.frameEncoder(maskPayload);
}

@Override
public WebSocketFrameDecoder decoder(WebSocketDecoderConfig config) {
return WebSocketProtocol.frameDecoder(
config.maxFramePayloadLength(), config.expectMaskedFrames(), config.allowMaskMismatch());
}

@Override
public void validate(boolean maskPayload, WebSocketDecoderConfig config) {
WebSocketProtocol.validateDecoderConfig(config);
}
}
25 changes: 0 additions & 25 deletions netty-websocket-http2-example/gradle.lockfile

This file was deleted.

71 changes: 60 additions & 11 deletions netty-websocket-http2-perftest/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,76 @@ plugins {

description = "Netty based implementation of rfc8441 - bootstrapping websockets with http/2. Performance test project"

def epollClassifier =
osdetector.os == "linux"
? "::${osdetector.classifier}"
: ""

dependencies {
implementation project(":netty-websocket-http2")
implementation "org.hdrhistogram:HdrHistogram"
implementation project(":netty-websocket-http2-callbacks-codec")
implementation "io.netty:netty-transport-classes-epoll"
implementation "io.netty:netty-transport-classes-kqueue"
implementation "org.hdrhistogram:HdrHistogram"
implementation "org.slf4j:slf4j-api"
runtimeOnly "io.netty:netty-transport-native-epoll${epollClassifier}"

runtimeOnly "io.netty:netty-tcnative-boringssl-static::${osdetector.classifier}"
runtimeOnly "ch.qos.logback:logback-classic"
if (osdetector.os == "linux") {
runtimeOnly "io.netty:netty-transport-native-epoll::${osdetector.classifier}"
} else if (osdetector.os == "osx") {
runtimeOnly "io.netty:netty-transport-native-kqueue::${osdetector.classifier}"
}
}

task runServerMessages(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.server.Main"
}

task runClientMessages(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main"
}

task runServer(type: JavaExec) {
task runServerCallbacks(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.server.Main"
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.server.Main"
}

task runClient(type: JavaExec) {
task runClientCallbacks(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.client.Main"
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main"
}

task serverMessagesScripts(type: CreateStartScripts) {
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.server.Main"
applicationName = "${project.name}-messages-server"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
}

task clientMessagesScripts(type: CreateStartScripts) {
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.messagecodec.client.Main"
applicationName = "${project.name}-messages-client"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
}

task serverCallbacksScripts(type: CreateStartScripts) {
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.server.Main"
applicationName = "${project.name}-callbacks-server"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
}

task clientCallbacksScripts(type: CreateStartScripts) {
mainClass = "com.jauntsdn.netty.handler.codec.http2.websocketx.perftest.callbackscodec.client.Main"
applicationName = "${project.name}-callbacks-client"
classpath = startScripts.classpath
outputDir = startScripts.outputDir
}

startScripts.dependsOn serverMessagesScripts
startScripts.dependsOn clientMessagesScripts
startScripts.dependsOn serverCallbacksScripts
startScripts.dependsOn clientCallbacksScripts

tasks.named("startScripts") {
enabled = false
}
28 changes: 0 additions & 28 deletions netty-websocket-http2-perftest/gradle.lockfile

This file was deleted.

Loading

0 comments on commit 4e37a0e

Please sign in to comment.