🚀 lightweight high-performance WebSocket framework ( 轻量级、高性能的WebSocket框架)
Java
1,936
123 commits
updated Sep 25, 2026
中文文档 (Chinese Docs)
netty-websocket-spring-boot-starter will help you develop WebSocket server by using Netty in spring-boot,it is easy to develop by using annotation like spring-websocket
<dependency>
<groupId>org.yeauty</groupId>
<artifactId>netty-websocket-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
@Component or manual exporter registration is needed.package com.example.demo;
import org.yeauty.annotation.OnMessage;
import org.yeauty.annotation.ServerEndpoint;
import org.yeauty.pojo.Session;
@ServerEndpoint(path = "/ws", port = "${ws.port:8081}")
public class MyWebSocket {
@OnMessage
public void onMessage(Session session, String message) {
session.sendText(message);
}
}
@SpringBootApplication and run this in the browser console:const ws = new WebSocket("ws://127.0.0.1:8081/ws");
ws.onopen = () => ws.send("hello");
ws.onmessage = event => console.log(event.data); // hello
WebSocket uses its own Netty listener; server.port does not configure it automatically. This example defaults to 8081, overridable with ws.port; the annotation's default remains 80 for compatibility. Use separate ports for separate servers. For endpoints outside the main package, add @EnableWebSocket(scanBasePackages = "com.example.endpoints") to a configuration class.
declaring
ServerEndpointExporterin Spring configuration,it will scan for WebSocket endpoints that be annotated withServerEndpoint. beans that be annotated withServerEndpointwill be registered as a WebSocket endpoint. all configurations are inside this annotation ( e.g.@ServerEndpoint("/ws"))
when there is a connection accepted,the method annotated with
@BeforeHandshakewill be called
classes which be injected to the method are:Session,HttpHeaders...
when there is a WebSocket connection completed,the method annotated with
@OnOpenwill be called
classes which be injected to the method are:Session,HttpHeaders...
when a WebSocket connection closed,the method annotated with
@OnClosewill be called classes which be injected to the method are:Session
when a WebSocket connection throw Throwable, the method annotated with
@OnErrorwill be called classes which be injected to the method are:Session,Throwable
when a WebSocket connection received a message,the method annotated with
@OnMessagewill be called classes which be injected to the method are:Session,String
when a WebSocket connection received the binary,the method annotated with
@OnBinarywill be called classes which be injected to the method are:Session,byte[]
when a WebSocket connection received the event of Netty,the method annotated with
@OnEventwill be called classes which be injected to the method are:Session,Object
all configurations are configured in
@ServerEndpoint's property
| property | default | description |
|---|---|---|
| path | "/" | path of WebSocket can be aliased for value |
| host | "0.0.0.0" | host of WebSocket."0.0.0.0" means all of local addresses |
| port | 80 | port of WebSocket。if the port equals to 0,it will use a random and available port(to get the port Multi-Endpoint) |
| bossLoopGroupThreads | 1 | num of threads in bossEventLoopGroup |
| workerLoopGroupThreads | 0 | num of threads in workerEventLoopGroup |
| useCompressionHandler | false | whether add WebSocketServerCompressionHandler to pipeline |
| optionConnectTimeoutMillis | 30000 | the same as ChannelOption.CONNECT_TIMEOUT_MILLIS in Netty |
| optionSoBacklog | 128 | the same as ChannelOption.SO_BACKLOG in Netty |
| childOptionWriteSpinCount | 16 | the same as ChannelOption.WRITE_SPIN_COUNT in Netty |
| childOptionWriteBufferHighWaterMark | 64*1024 | the same as ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK in Netty,but use ChannelOption.WRITE_BUFFER_WATER_MARK in fact. |
| childOptionWriteBufferLowWaterMark | 32*1024 | the same as ChannelOption.WRITE_BUFFER_LOW_WATER_MARK in Netty,but use ChannelOption.WRITE_BUFFER_WATER_MARK in fact. |
| childOptionSoRcvbuf | -1(mean not set) | the same as ChannelOption.SO_RCVBUF in Netty |
| childOptionSoSndbuf | -1(mean not set) | the same as ChannelOption.SO_SNDBUF in Netty |
| childOptionTcpNodelay | true | the same as ChannelOption.TCP_NODELAY in Netty |
| childOptionSoKeepalive | false | the same as ChannelOption.SO_KEEPALIVE in Netty |
| childOptionSoLinger | -1 | the same as ChannelOption.SO_LINGER in Netty |
| childOptionAllowHalfClosure | false | the same as ChannelOption.ALLOW_HALF_CLOSURE in Netty |
| readerIdleTimeSeconds | 0 | the same as readerIdleTimeSeconds in IdleStateHandler and add IdleStateHandler to pipeline when it is not 0 |
| writerIdleTimeSeconds | 0 | the same as writerIdleTimeSeconds in IdleStateHandler and add IdleStateHandler to pipeline when it is not 0 |
| allIdleTimeSeconds | 0 | the same as allIdleTimeSeconds in IdleStateHandler and add IdleStateHandler to pipeline when it is not 0 |
| maxFramePayloadLength | 65536 | Maximum allowable frame payload length. |
| maxMessagePayloadLength | 1048576 (1 MiB) | Maximum decoded message size in bytes, including all fragments; also bounds decompression workspace (see lifecycle notes). Must be positive; raise explicitly for larger application messages. |
| useEventExecutorGroup | true | Dispatch message, close and event callbacks to a business executor; handshake and open callbacks still run on the I/O thread |
| eventExecutorGroupThreads | 16 | Number of business executor threads |
| sslKeyPassword | ""(mean not set) | the same as server.ssl.key-password in spring-boot |
| sslKeyStore | ""(mean not set) | the same as server.ssl.key-store in spring-boot |
| sslKeyStorePassword | ""(mean not set) | the same as server.ssl.key-store-password in spring-boot |
| sslKeyStoreType | ""(mean not set) | the same as server.ssl.key-store-type in spring-boot |
| sslTrustStore | ""(mean not set) | the same as server.ssl.trust-store in spring-boot |
| sslTrustStorePassword | ""(mean not set) | the same as server.ssl.trust-store-password in spring-boot |
| sslTrustStoreType | ""(mean not set) | the same as server.ssl.trust-store-type in spring-boot |
| corsOrigins | {}(mean not set) | the same as @CrossOrigin#origins in spring-boot |
| corsAllowCredentials | ""(mean not set) | the same as @CrossOrigin#allowCredentials in spring-boot |
You can get the configurate of
application.propertiesby using${...}placeholders. for example:
${...} in @ServerEndpoint@ServerEndpoint(host = "${ws.host}",port = "${ws.port}")
public class MyWebSocket {
...
}
application.propertiesws.host=0.0.0.0
ws.port=8081
The way of configure favicon is the same as spring-boot.If favicon.ico is presented in the root of the classpath,it will be automatically used as the favicon of the application.the example is following:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- favicon.ico
The way of configure favicon is the same as spring-boot.you can add a file to an /public/error
folder.The name of the error page should be the exact status code or a series mask.the example is following:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
| +- 5xx.html
+- <other public assets>
@ServerEndpoint to each endpoint class; @Component is not required.port="0" share a random port only on the same host. Query it after startup with ServerEndpointConfig.getRandomPort(host); the cache is cleared on shutdown.0.0.0.0 binds all local addresses; avoid overlapping listeners on the same port with another host.Use this project's org.yeauty.annotation.RequestParam and PathVariable, rather than similarly named Spring MVC or Jakarta annotations. Explicit names are recommended:
@ServerEndpoint(path = "/ws/{room}", port = "8081")
public class RoomSocket {
@OnOpen
public void onOpen(Session session,
@PathVariable("room") String room,
@RequestParam(value = "name", defaultValue = "guest") String name) {
session.sendText(room + ":" + name);
}
}
Connect to ws://127.0.0.1:8081/ws/lobby?name=Alice; absent or empty name uses guest. Use the imports from Quick Start, plus this project's OnOpen, PathVariable and RequestParam.
required=true is the default: a missing parameter rejects the handshake with HTTP 400, as does a conversion failure. required=false permits null; use wrapper types such as Integer for optional numbers.defaultValue covers absent or empty scalar parameters and implicitly makes them optional. An empty String without a default is still a supplied value; applications own non-blank validation.@RequestParam("tag") List<String>; for all parameters use @RequestParam Map<String, String> or MultiValueMap<String, String>.String for the text body.-parameters in the application's own Maven/Gradle compilation. The starter's compiler settings do not propagate.@BeforeHandshake, call session.close() to reject a connection. A thrown callback exception aborts the handshake. Declare supported subprotocols with session.setSubprotocols("chat"); the server negotiates a single supported protocol from the client's offer.@BeforeHandshake and @OnOpen run on the I/O thread and should return promptly. With the default useEventExecutorGroup=true, message, close and event callbacks run on the business executor. Size this pool for the workload and avoid long blocking operations. The thread for @OnError depends on the source of the error.
maxFramePayloadLength limits individual frames. The new maxMessagePayloadLength defaults to 1 MiB and bounds decoded messages and fragment aggregation. Decompression allocation is bounded separately at the message limit plus max(8192, 2 × maxFramePayloadLength) bytes of decoder workspace (capped at Integer.MAX_VALUE). Oversized decoded/aggregated messages close the connection with code 1009; decompression failures also disconnect. Configure both limits deliberately for large messages, for example:
@ServerEndpoint(path = "/upload", port = "8081",
maxFramePayloadLength = "1048576",
maxMessagePayloadLength = "8388608")
Bind and TLS initialization failures fail application startup. Closing the Spring context stops listening, sends WebSocket close frames, closes connections, then terminates network and business executors in order. The caller waits up to 5 seconds per listener; timeouts are logged, with late network events still preceding business-executor shutdown. Long-running user callbacks can delay actual resource termination; allow for this in deployment termination grace periods. Do not expect a bye text message.
This component owns a separate listener; server.ssl.* is not applied automatically. Map shared properties explicitly:
@ServerEndpoint(path = "/ws", port = "${ws.port:8443}",
sslKeyStore = "${server.ssl.key-store}",
sslKeyStorePassword = "${server.ssl.key-store-password}",
sslKeyStoreType = "${server.ssl.key-store-type:PKCS12}")
AutoConfiguration.imports; normal Boot applications need no manual exporter bean.required=false or supply defaults.maxMessagePayloadLength explicitly for messages over 1 MiB; keep all settings consistent across endpoints sharing a listener.Use JDK 17 or 21. The included Maven Wrapper pins Maven 3.9.11; use mvnw.cmd on Windows. Run from the repository root:
./mvnw -B -ntp clean verify
./mvnw -B -ntp -Pconsumer-test verify
./mvnw -B -ntp -Prelease -Dgpg.skip=true verify
The consumer test creates an independent Boot application from the packaged starter and verifies startup, messages and shutdown with a single application dependency. See RELEASING.md for signing and Central Portal publishing. The release verification above skips signing and does not upload.
@PathVariable@RequestParam from queryParameterMap ,instead of @RequestParam MultiValueMap@BeforeHandshake annotation,you can close the connect before handshake@BeforeHandshake event@Component on endpoint classNetty version to 4.1.44.Final@RequestParam MultiValueMap to get valueNetty version to 4.1.45.FinalServerEndpointExporter manully@BeforeHandshake , NullPointerException will appear@BeforeHandshake , Session in OnOpen is nullThrowable in OnError event is nullbossLoopGroupThreads to 1useEventExecutorGroup to run synchronous and time-consuming business logic in EventExecutorGroup, so that the I/O thread is not blocked by a time-consuming taskNetty version to 4.1.49.FinalServerEndpoint class is proxied by CGLIB (as with AOP enhancement), it still works@enableWebSocket adds the scanBasePackages attribute@serverEndpoint no longer depends on @ComponentNetty version to 4.1.67.FinalSec-WebSocket-Protocol in the response header.bye text message.Netty version to 4.1.118.Final.Netty version to 4.2.18.Final.META-INF/spring.factories to META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.Overall
Maintained
Code review
Latest release
Releases this year
11,076 followers · starred Dec 2020
412 followers · starred Mar 2021
1 followers · starred Jan 2022
8 followers · starred Aug 2025
🚀 lightweight high-performance WebSocket framework ( 轻量级、高性能的WebSocket框架)
Java
1,936
123 commits
updated Sep 25, 2026
中文文档 (Chinese Docs)
netty-websocket-spring-boot-starter will help you develop WebSocket server by using Netty in spring-boot,it is easy to develop by using annotation like spring-websocket
<dependency>
<groupId>org.yeauty</groupId>
<artifactId>netty-websocket-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
@Component or manual exporter registration is needed.package com.example.demo;
import org.yeauty.annotation.OnMessage;
import org.yeauty.annotation.ServerEndpoint;
import org.yeauty.pojo.Session;
@ServerEndpoint(path = "/ws", port = "${ws.port:8081}")
public class MyWebSocket {
@OnMessage
public void onMessage(Session session, String message) {
session.sendText(message);
}
}
@SpringBootApplication and run this in the browser console:const ws = new WebSocket("ws://127.0.0.1:8081/ws");
ws.onopen = () => ws.send("hello");
ws.onmessage = event => console.log(event.data); // hello
WebSocket uses its own Netty listener; server.port does not configure it automatically. This example defaults to 8081, overridable with ws.port; the annotation's default remains 80 for compatibility. Use separate ports for separate servers. For endpoints outside the main package, add @EnableWebSocket(scanBasePackages = "com.example.endpoints") to a configuration class.
declaring
ServerEndpointExporterin Spring configuration,it will scan for WebSocket endpoints that be annotated withServerEndpoint. beans that be annotated withServerEndpointwill be registered as a WebSocket endpoint. all configurations are inside this annotation ( e.g.@ServerEndpoint("/ws"))
when there is a connection accepted,the method annotated with
@BeforeHandshakewill be called
classes which be injected to the method are:Session,HttpHeaders...
when there is a WebSocket connection completed,the method annotated with
@OnOpenwill be called
classes which be injected to the method are:Session,HttpHeaders...
when a WebSocket connection closed,the method annotated with
@OnClosewill be called classes which be injected to the method are:Session
when a WebSocket connection throw Throwable, the method annotated with
@OnErrorwill be called classes which be injected to the method are:Session,Throwable
when a WebSocket connection received a message,the method annotated with
@OnMessagewill be called classes which be injected to the method are:Session,String
when a WebSocket connection received the binary,the method annotated with
@OnBinarywill be called classes which be injected to the method are:Session,byte[]
when a WebSocket connection received the event of Netty,the method annotated with
@OnEventwill be called classes which be injected to the method are:Session,Object
all configurations are configured in
@ServerEndpoint's property
| property | default | description |
|---|---|---|
| path | "/" | path of WebSocket can be aliased for value |
| host | "0.0.0.0" | host of WebSocket."0.0.0.0" means all of local addresses |
| port | 80 | port of WebSocket。if the port equals to 0,it will use a random and available port(to get the port Multi-Endpoint) |
| bossLoopGroupThreads | 1 | num of threads in bossEventLoopGroup |
| workerLoopGroupThreads | 0 | num of threads in workerEventLoopGroup |
| useCompressionHandler | false | whether add WebSocketServerCompressionHandler to pipeline |
| optionConnectTimeoutMillis | 30000 | the same as ChannelOption.CONNECT_TIMEOUT_MILLIS in Netty |
| optionSoBacklog | 128 | the same as ChannelOption.SO_BACKLOG in Netty |
| childOptionWriteSpinCount | 16 | the same as ChannelOption.WRITE_SPIN_COUNT in Netty |
| childOptionWriteBufferHighWaterMark | 64*1024 | the same as ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK in Netty,but use ChannelOption.WRITE_BUFFER_WATER_MARK in fact. |
| childOptionWriteBufferLowWaterMark | 32*1024 | the same as ChannelOption.WRITE_BUFFER_LOW_WATER_MARK in Netty,but use ChannelOption.WRITE_BUFFER_WATER_MARK in fact. |
| childOptionSoRcvbuf | -1(mean not set) | the same as ChannelOption.SO_RCVBUF in Netty |
| childOptionSoSndbuf | -1(mean not set) | the same as ChannelOption.SO_SNDBUF in Netty |
| childOptionTcpNodelay | true | the same as ChannelOption.TCP_NODELAY in Netty |
| childOptionSoKeepalive | false | the same as ChannelOption.SO_KEEPALIVE in Netty |
| childOptionSoLinger | -1 | the same as ChannelOption.SO_LINGER in Netty |
| childOptionAllowHalfClosure | false | the same as ChannelOption.ALLOW_HALF_CLOSURE in Netty |
| readerIdleTimeSeconds | 0 | the same as readerIdleTimeSeconds in IdleStateHandler and add IdleStateHandler to pipeline when it is not 0 |
| writerIdleTimeSeconds | 0 | the same as writerIdleTimeSeconds in IdleStateHandler and add IdleStateHandler to pipeline when it is not 0 |
| allIdleTimeSeconds | 0 | the same as allIdleTimeSeconds in IdleStateHandler and add IdleStateHandler to pipeline when it is not 0 |
| maxFramePayloadLength | 65536 | Maximum allowable frame payload length. |
| maxMessagePayloadLength | 1048576 (1 MiB) | Maximum decoded message size in bytes, including all fragments; also bounds decompression workspace (see lifecycle notes). Must be positive; raise explicitly for larger application messages. |
| useEventExecutorGroup | true | Dispatch message, close and event callbacks to a business executor; handshake and open callbacks still run on the I/O thread |
| eventExecutorGroupThreads | 16 | Number of business executor threads |
| sslKeyPassword | ""(mean not set) | the same as server.ssl.key-password in spring-boot |
| sslKeyStore | ""(mean not set) | the same as server.ssl.key-store in spring-boot |
| sslKeyStorePassword | ""(mean not set) | the same as server.ssl.key-store-password in spring-boot |
| sslKeyStoreType | ""(mean not set) | the same as server.ssl.key-store-type in spring-boot |
| sslTrustStore | ""(mean not set) | the same as server.ssl.trust-store in spring-boot |
| sslTrustStorePassword | ""(mean not set) | the same as server.ssl.trust-store-password in spring-boot |
| sslTrustStoreType | ""(mean not set) | the same as server.ssl.trust-store-type in spring-boot |
| corsOrigins | {}(mean not set) | the same as @CrossOrigin#origins in spring-boot |
| corsAllowCredentials | ""(mean not set) | the same as @CrossOrigin#allowCredentials in spring-boot |
You can get the configurate of
application.propertiesby using${...}placeholders. for example:
${...} in @ServerEndpoint@ServerEndpoint(host = "${ws.host}",port = "${ws.port}")
public class MyWebSocket {
...
}
application.propertiesws.host=0.0.0.0
ws.port=8081
The way of configure favicon is the same as spring-boot.If favicon.ico is presented in the root of the classpath,it will be automatically used as the favicon of the application.the example is following:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- favicon.ico
The way of configure favicon is the same as spring-boot.you can add a file to an /public/error
folder.The name of the error page should be the exact status code or a series mask.the example is following:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
| +- 5xx.html
+- <other public assets>
@ServerEndpoint to each endpoint class; @Component is not required.port="0" share a random port only on the same host. Query it after startup with ServerEndpointConfig.getRandomPort(host); the cache is cleared on shutdown.0.0.0.0 binds all local addresses; avoid overlapping listeners on the same port with another host.Use this project's org.yeauty.annotation.RequestParam and PathVariable, rather than similarly named Spring MVC or Jakarta annotations. Explicit names are recommended:
@ServerEndpoint(path = "/ws/{room}", port = "8081")
public class RoomSocket {
@OnOpen
public void onOpen(Session session,
@PathVariable("room") String room,
@RequestParam(value = "name", defaultValue = "guest") String name) {
session.sendText(room + ":" + name);
}
}
Connect to ws://127.0.0.1:8081/ws/lobby?name=Alice; absent or empty name uses guest. Use the imports from Quick Start, plus this project's OnOpen, PathVariable and RequestParam.
required=true is the default: a missing parameter rejects the handshake with HTTP 400, as does a conversion failure. required=false permits null; use wrapper types such as Integer for optional numbers.defaultValue covers absent or empty scalar parameters and implicitly makes them optional. An empty String without a default is still a supplied value; applications own non-blank validation.@RequestParam("tag") List<String>; for all parameters use @RequestParam Map<String, String> or MultiValueMap<String, String>.String for the text body.-parameters in the application's own Maven/Gradle compilation. The starter's compiler settings do not propagate.@BeforeHandshake, call session.close() to reject a connection. A thrown callback exception aborts the handshake. Declare supported subprotocols with session.setSubprotocols("chat"); the server negotiates a single supported protocol from the client's offer.@BeforeHandshake and @OnOpen run on the I/O thread and should return promptly. With the default useEventExecutorGroup=true, message, close and event callbacks run on the business executor. Size this pool for the workload and avoid long blocking operations. The thread for @OnError depends on the source of the error.
maxFramePayloadLength limits individual frames. The new maxMessagePayloadLength defaults to 1 MiB and bounds decoded messages and fragment aggregation. Decompression allocation is bounded separately at the message limit plus max(8192, 2 × maxFramePayloadLength) bytes of decoder workspace (capped at Integer.MAX_VALUE). Oversized decoded/aggregated messages close the connection with code 1009; decompression failures also disconnect. Configure both limits deliberately for large messages, for example:
@ServerEndpoint(path = "/upload", port = "8081",
maxFramePayloadLength = "1048576",
maxMessagePayloadLength = "8388608")
Bind and TLS initialization failures fail application startup. Closing the Spring context stops listening, sends WebSocket close frames, closes connections, then terminates network and business executors in order. The caller waits up to 5 seconds per listener; timeouts are logged, with late network events still preceding business-executor shutdown. Long-running user callbacks can delay actual resource termination; allow for this in deployment termination grace periods. Do not expect a bye text message.
This component owns a separate listener; server.ssl.* is not applied automatically. Map shared properties explicitly:
@ServerEndpoint(path = "/ws", port = "${ws.port:8443}",
sslKeyStore = "${server.ssl.key-store}",
sslKeyStorePassword = "${server.ssl.key-store-password}",
sslKeyStoreType = "${server.ssl.key-store-type:PKCS12}")
AutoConfiguration.imports; normal Boot applications need no manual exporter bean.required=false or supply defaults.maxMessagePayloadLength explicitly for messages over 1 MiB; keep all settings consistent across endpoints sharing a listener.Use JDK 17 or 21. The included Maven Wrapper pins Maven 3.9.11; use mvnw.cmd on Windows. Run from the repository root:
./mvnw -B -ntp clean verify
./mvnw -B -ntp -Pconsumer-test verify
./mvnw -B -ntp -Prelease -Dgpg.skip=true verify
The consumer test creates an independent Boot application from the packaged starter and verifies startup, messages and shutdown with a single application dependency. See RELEASING.md for signing and Central Portal publishing. The release verification above skips signing and does not upload.
@PathVariable@RequestParam from queryParameterMap ,instead of @RequestParam MultiValueMap@BeforeHandshake annotation,you can close the connect before handshake@BeforeHandshake event@Component on endpoint classNetty version to 4.1.44.Final@RequestParam MultiValueMap to get valueNetty version to 4.1.45.FinalServerEndpointExporter manully@BeforeHandshake , NullPointerException will appear@BeforeHandshake , Session in OnOpen is nullThrowable in OnError event is nullbossLoopGroupThreads to 1useEventExecutorGroup to run synchronous and time-consuming business logic in EventExecutorGroup, so that the I/O thread is not blocked by a time-consuming taskNetty version to 4.1.49.FinalServerEndpoint class is proxied by CGLIB (as with AOP enhancement), it still works@enableWebSocket adds the scanBasePackages attribute@serverEndpoint no longer depends on @ComponentNetty version to 4.1.67.FinalSec-WebSocket-Protocol in the response header.bye text message.Netty version to 4.1.118.Final.Netty version to 4.2.18.Final.META-INF/spring.factories to META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.Overall
Maintained
Code review
Latest release
Releases this year
11,076 followers · starred Dec 2020
412 followers · starred Mar 2021
1 followers · starred Jan 2022
8 followers · starred Aug 2025