Achieve WebSocket Load Balancing Across Microservice Instances with a Single Annotation

This article explains how to solve the WebSocket message delivery problem in microservice architectures by using a lightweight library that provides an @EnableWebSocketLoadBalanceConcept annotation, detailing its design, connection management, message routing, and customizable selectors for targeted delivery.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Achieve WebSocket Load Balancing Across Microservice Instances with a Single Annotation

Introduction

WebSocket works in monolithic apps, but in a microservice architecture a client connected to one instance may miss messages broadcast by another instance. Example: service A has instances A1 and A2; a client routed to A1 will not receive messages sent by A2.

The library forwards messages between instances so that any instance can deliver messages to all connected clients with a single configuration annotation.

Repository: https://github.com/Linyuzai/concept/wiki/Concept-WebSocket-LoadBalance

Usage

Add the annotation @EnableWebSocketLoadBalanceConcept together with @EnableDiscoveryClient and @SpringBootApplication on the Spring Boot main class:

@EnableWebSocketLoadBalanceConcept
@EnableDiscoveryClient
@SpringBootApplication
public class AServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(AServiceApplication.class, args);
    }
}

Inject WebSocketLoadBalanceConcept where messages need to be sent:

@RestController
@RequestMapping("/ws")
public class WsController {
    @Autowired
    private WebSocketLoadBalanceConcept concept;

    @RequestMapping("/send")
    public void send(@RequestParam String msg) {
        concept.send(msg);
    }
}

Abstract Design

The problem is modeled as a “long‑connection cluster”. Both WebSocket and TCP are treated as long‑connection protocols. A top‑level Connection interface is defined with concrete implementations such as WebSocketConnection and TCPConnection. Short‑lived protocols can also be wrapped if needed.

Forwarding Strategy

Each service instance opens client connections to the server endpoints of all other instances (WebSocket client to other WebSocket servers, TCP client to other TCP servers). When an instance receives a message, it forwards the message through these pseudo‑client connections, and the remote instance delivers it to its real clients.

Connection Establishment

Service discovery (Eureka, Nacos, etc.) provides the list of peer instances. Using DiscoveryClient#getInstances(serviceId), the library obtains all instances except itself and initiates connections. After a connection is established, each side exchanges its instance information to create the reciprocal connection, guaranteeing bidirectional links.

Heartbeat detection and automatic reconnection are built in: missed heartbeats close the connection, and the library periodically re‑queries the registry to reconnect missing peers.

Connection flow diagram
Connection flow diagram

Connection Management

Connections are classified into real client connections and forwarding connections. A ConnectionFactory adapts any concrete connection into a unified Connection object. Each connection is paired with a MessageEncoder and MessageDecoder. MessageCodecAdapter allows custom codecs for different connection types.

Message Sending and Selection

When a message is sent, a ConnectionSelector determines which connections should receive it. Custom selectors can implement precise routing similar to Ribbon’s IRule.

Two built‑in selectors are provided:

UserSelector : Sends a message only to connections whose metadata contains a matching userId header.

PathSelector : Routes messages based on a topic‑like path stored in connection metadata and message header.

Metadata and headers are simple key‑value maps, making it easy to extend the routing logic.

Conclusion

The library abstracts long‑connection clustering, manages peer connections, and provides flexible message routing with minimal configuration. By leveraging Spring Cloud discovery and a few annotations, developers can achieve transparent WebSocket load balancing across multiple service instances.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Distributed SystemsJavaMicroservicesload balancingWebSocketannotationSpring Cloud
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.