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.

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

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.

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.

JavaWebSocketSpring 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.