Achieve WebSocket Load Balancing Across Microservice Instances with a Single Annotation
This article explains the challenges of using WebSocket in a microservice environment, presents a lightweight library that abstracts long‑connection clustering, shows how to enable it with @EnableWebSocketLoadBalanceConcept, and details the underlying architecture, selectors, and heartbeat mechanisms for reliable message broadcasting.
Introduction
WebSocket works fine in monolithic applications, but in a microservice architecture a client may connect to one instance while another instance needs to broadcast a message, causing the client to miss the message.
For example, service A has instances A1 and A2; the WebSocket client C connects via the gateway to A1. When A2 wants to send a message to all clients, C will not receive it because the connection is bound to A1.
The simplest fix is to forward A2's message to A1, which then forwards it to C. The author built a library that automates this process with a single configuration annotation.
Usage
Add the annotation @EnableWebSocketLoadBalanceConcept (together with @EnableDiscoveryClient) to the Spring Boot entry class.
@EnableWebSocketLoadBalanceConcept
@EnableDiscoveryClient
@SpringBootApplication
public class AServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AServiceApplication.class, args);
}
}Inject WebSocketLoadBalanceConcept wherever a message needs to be sent:
@RestController
@RequestMapping("/ws")
public class WsController {
@Autowired
private WebSocketLoadBalanceConcept concept;
@RequestMapping("/send")
public void send(@RequestParam String msg) {
concept.send(msg);
}
}This usage reduces cross‑instance message forwarding to a single line of code.
Abstract Design
The problem is generalized to a “clustered long‑connection scheme”. Both WebSocket and TCP are treated as concrete implementations of a generic Connection interface.
Implementations: WebSocketConnection, TCPConnection, optionally HTTPConnection.
A ConnectionSubscriber represents a service instance that subscribes to messages from other instances. The default implementation uses the native protocol; custom implementations can use MQ, HTTP, etc.
Service discovery is handled by ConnectionServerManager. When Spring Cloud discovery (Eureka, Nacos, etc.) is present, the manager obtains all instances via DiscoveryClient#getInstances and excludes the local instance.
Connection Lifecycle
Each instance establishes outbound connections to every other instance, acting as a client. When a connection is established, the instance sends its own metadata; the remote side reciprocates, ensuring bidirectional links.
Heartbeat detection and automatic reconnection keep the mesh resilient. If a heartbeat is missed, the connection is closed and the instance re‑queries the registry to rebuild missing links.
Message Routing
Messages are encoded and decoded by MessageEncoder and MessageDecoder. Different connection types use different codecs, coordinated by a MessageCodecAdapter.
A ConnectionSelector decides which connections should receive a particular message. Built‑in selectors include: UserSelector / UserMessage – send to a specific user identified by a userId header. PathSelector / PathMessage – send to connections whose subscription path matches a message header.
Custom selectors can inspect connection metadata or message headers to implement fine‑grained routing.
Putting It All Together
The library’s core class ConnectionLoadBalanceConcept orchestrates sending: it obtains the appropriate Connection objects from the ConnectionRepository, applies the selector, and forwards the payload.
Concrete implementations such as DiscoveryConnectionServerManager, ReactiveWebSocketConnection, and the various selectors demonstrate how the abstract model maps to Spring Cloud discovery and Reactor WebSocket components.
Conclusion
By abstracting long‑lived connections into a small set of interfaces, the design provides a reusable, extensible solution for reliable message broadcasting in clustered microservices while keeping application code minimal.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
