WebSocket Load‑Balancing Concept for Microservice Architectures
This article explains the challenges of using WebSocket in a microservice environment, introduces a load‑balancing library that forwards messages between service instances, shows how to enable it with Spring annotations, and details the underlying connection, subscription, and selector mechanisms for reliable message routing.
When a WebSocket client connects through a gateway in a microservice setup, load‑balancing may route the connection to one instance (e.g., A1) while another instance (A2) needs to broadcast a message, causing the client to miss the broadcast.
The simplest fix is to forward messages from the sending instance (A2) to the instance that holds the client connection (A1), which then delivers the message to the client.
Usage : Add the annotation @EnableWebSocketLoadBalanceConcept (together with @EnableDiscoveryClient and @SpringBootApplication ) to the Spring Boot entry class, then inject WebSocketLoadBalanceConcept into any component that needs to send messages, as shown in the sample controller:
@RestController
@RequestMapping("/ws")
public class WsController {
@Autowired
private WebSocketLoadBalanceConcept concept;
@RequestMapping("/send")
public void send(@RequestParam String msg) {
concept.send(msg);
}
}The library abstracts long‑connection handling by defining a top‑level Connection interface with implementations such as WebSocketConnection and TCPConnection . It also provides a ConnectionSubscriber interface for services to subscribe to messages from other instances, with default implementations based on the native protocol and optional custom implementations (e.g., MQ, HTTP).
Service discovery (Spring Cloud, Eureka, Nacos, etc.) is used to obtain the list of peer instances via DiscoveryClient#getInstances . Each instance connects to its peers, exchanges instance‑info messages, and establishes bidirectional links. Heartbeat detection and automatic reconnection ensure resilience.
Connections are classified into three categories:
Client – regular client connections.
Subscriber – pseudo‑client connections used to receive forwarded messages.
Observable – pseudo‑client connections used to send forwarded messages.
A ConnectionFactory adapts any concrete connection to the generic Connection type, while MessageEncoder , MessageDecoder , and MessageCodecAdapter handle encoding/decoding for different connection kinds.
Message routing is performed by a ConnectionSelector . Built‑in selectors include UserSelector (for sending to a specific userId) and PathSelector (for topic‑based routing). Messages carry headers (e.g., userId ) that selectors use to match the appropriate connections.
For precise user targeting, the client sends its userId upon connection, which is stored in the connection’s metadata. When a message includes the same userId header, the selector routes the message to the matching connection, avoiding unnecessary forwarding.
Path‑based routing works similarly: different subscription paths are represented by connection URLs, and the PathSelector matches messages whose header path equals the connection’s path.
In summary, the library provides a modular, extensible framework for load‑balancing long‑lived connections (WebSocket/TCP) across multiple service instances, leveraging Spring Cloud discovery, customizable connection factories, and flexible selectors to achieve reliable, targeted message delivery.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
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.