Skip to content

Add WebSocket server metrics - #4317

Open
LivingLikeKrillin wants to merge 4 commits into
reactor:1.3.xfrom
LivingLikeKrillin:feature/websocket-server-metrics-core
Open

LivingLikeKrillin wants to merge 4 commits into
reactor:1.3.xfrom
LivingLikeKrillin:feature/websocket-server-metrics-core

Conversation

@LivingLikeKrillin

Copy link
Copy Markdown
Contributor

Summary

Add WebSocket server metrics, mirroring the client WebSocket metrics (#4118). This is the first step of #4305. The meters are registered under the reactor.netty.websocket.server prefix, tagged with uri (mapped through the configured uriTagValue when one is set, as for the HTTP server meters):

  • handshake.time (tags uri, status): 101 on a successful HTTP/1.1 upgrade, 200 on a successful HTTP/2 Extended CONNECT, ERROR when the handshake fails after the metrics handler is installed. Pre-upgrade rejections (unsupported version on HTTP/1.1, invalid Extended CONNECT on HTTP/2) are answered before installation and record no WebSocket meter
  • connection.duration: from handler installation on upgrade to pipeline removal on close, so it includes the handshake and is also recorded when the handshake fails (same as the client)
  • data.received, data.sent, data.received.time, data.sent.time: recorded once per message on the final fragment; control frames (Close, Ping, Pong) are excluded
  • errors: pipeline exceptions and application errors raised while sending the WebSocket outbound

On the Micrometer path — as on the client — the handshake meter is produced by a reactor.netty.websocket.server.handshake.time observation instead of a direct recorder call.

Changes

On upgrade, WebsocketServerOperations (HTTP/1.1) and Http2WebsocketServerOperations (HTTP/2) install a WebSocket metrics handler at the WsMetricsHandler slot, selected from the configured HTTP metrics handler the same way the client's swapMetricsHandler does. The Micrometer path always uses the built-in WebSocket recorder; on the other two, a recorder that does not implement the matching WebSocket type (ContextAwareWebSocketServerMetricsRecorder on the context-aware path, WebSocketServerMetricsRecorder on the plain one) is wrapped in an adapter that delegates the inherited HTTP methods and no-ops the two WebSocket ones. The WebsocketHttpServerMetricsHandler pass-through is kept unchanged, since it carries the connection/stream count accounting (#4290); the new handler is additive.

New public API: WebSocketServerMetricsRecorder (extends HttpServerMetricsRecorder, two methods) and ContextAwareWebSocketServerMetricsRecorder (adds the two ContextView overloads), plus Metrics.WEBSOCKET_SERVER_PREFIX. All other new types are package-private, and no meter of the built-in Micrometer recorder carries a remote.address or proxy.address tag. Native-image reflection configuration for the four new handlers and a reference documentation section are included.

Test plan

  • Handshake time on HTTP/1.1 (101) and HTTP/2 Extended CONNECT (200)
  • Connection duration recorded on close
  • Data metrics in both directions, byte counts and timings
  • Errors counted on the application-error path
  • ERROR handshake status when the handshake fails after the handler is installed
  • uriTagValue mapping applied to the uri tag
  • Control frames excluded from the data metrics, inbound and outbound
  • A fragmented inbound message recorded once, on its final fragment
  • A burst of messages recorded per message in both directions, with exact byte attribution

These cover the Micrometer recorder path. The custom-recorder path is exercised end to end by the existing testServerConnectionsWebsocketRecorder, but no test asserts the WebSocket recordings made on it, and the context-aware recorder path is not covered by a test yet.

Related to #4305

Signed-off-by: Jooyoung Jung <livinglikekrillin@gmail.com>
Signed-off-by: Jooyoung Jung <livinglikekrillin@gmail.com>
Signed-off-by: Jooyoung Jung <livinglikekrillin@gmail.com>
* @param status the WebSocket handshake status
* @param time the time in nanoseconds that is spent for the handshake
*/
public abstract void recordWebSocketHandshakeTime(ContextView contextView, SocketAddress remoteAddress, String uri,
* @param status the WebSocket handshake status
* @param time the time in nanoseconds that is spent for the handshake
*/
public abstract void recordWebSocketHandshakeTime(ContextView contextView, SocketAddress remoteAddress, String uri,
* @param status the WebSocket handshake status
* @param time the time in nanoseconds that is spent for the handshake
*/
public abstract void recordWebSocketHandshakeTime(ContextView contextView, SocketAddress remoteAddress, String uri,
* @param time the time in nanoseconds that is spent for the handshake
*/
public abstract void recordWebSocketHandshakeTime(ContextView contextView, SocketAddress remoteAddress, String uri,
String status, Duration time);
* @param time the time in nanoseconds that is spent for the handshake
*/
public abstract void recordWebSocketHandshakeTime(ContextView contextView, SocketAddress remoteAddress, String uri,
String status, Duration time);
* @param uri the requested URI
* @param time the duration of the connection
*/
public abstract void recordWebSocketConnectionDuration(ContextView contextView, SocketAddress remoteAddress,
* @param uri the requested URI
* @param time the duration of the connection
*/
public abstract void recordWebSocketConnectionDuration(ContextView contextView, SocketAddress remoteAddress,
* @param time the duration of the connection
*/
public abstract void recordWebSocketConnectionDuration(ContextView contextView, SocketAddress remoteAddress,
String uri, Duration time);
* @param time the duration of the connection
*/
public abstract void recordWebSocketConnectionDuration(ContextView contextView, SocketAddress remoteAddress,
String uri, Duration time);
@violetagg violetagg added the type/enhancement A general enhancement label Aug 6, 2026
@violetagg violetagg added this to the 1.3.8 milestone Aug 6, 2026
@violetagg violetagg linked an issue Aug 6, 2026 that may be closed by this pull request
The change is scheduled for 1.3.8, so the new WebSocket server metrics
types and Metrics.WEBSOCKET_SERVER_PREFIX now document that version.

Signed-off-by: Jooyoung Jung <livinglikekrillin@gmail.com>
@LivingLikeKrillin

Copy link
Copy Markdown
Contributor Author

Hi @violetagg, pushed a small fix: @since now says 1.3.8 rather than 1.3.7, which no longer holds now that 1.3.7 has shipped. Javadoc only.

This may be a lot to review in one pass. If smaller pieces would be easier, I am glad to split it myself: it divides cleanly along the three content commits (roughly 860, 710 and 620 lines), in whatever grouping suits you. If you would rather keep it as one PR, that works too.

@violetagg

Copy link
Copy Markdown
Member

Thanks!
I have this in my ToDo list and will make sure to merge it for 1.3.8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/enhancement A general enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for WebSocket server metrics

3 participants