Backend Development 17 min read

Implementing Distributed WebSocket Clusters with Spring Cloud, Consistent Hashing, and Gateway

This article explores practical approaches for building a distributed WebSocket cluster in a Spring Cloud environment, covering session handling, Netty versus Spring WebSocket implementations, session broadcasting, consistent‑hash routing, gateway configuration, Ribbon limitations, and provides code examples for each solution.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Implementing Distributed WebSocket Clusters with Spring Cloud, Consistent Hashing, and Gateway

The author describes a real‑world scenario where multiple users need to communicate via WebSocket in a clustered environment, involving four servers (one with SSL, a Redis+MySQL server, and two application servers) and strict SSL gateway requirements.

Technology stack: Eureka, Redis (session sharing and pub/sub), Spring Boot, Zuul, Spring Cloud Gateway, Spring WebSocket, Ribbon, Netty, and Consistent Hash algorithm.

WebSocketSession vs HttpSession: WebSocket sessions cannot be serialized to Redis, making true session sharing impossible, whereas HttpSession sharing is straightforward with spring-session-data-redis and spring-boot-starter-redis .

Solution evolution:

Netty implementation

protected void handleTextMessage(WebSocketSession session, TextMessage message) {
    System.out.println("服务器接收到的消息: " + message);
    // send message to client
    session.sendMessage(new TextMessage("message"));
}

Netty offers high concurrency but introduces integration challenges, duplicate business logic, and difficulty with service discovery and load balancing.

Spring WebSocket implementation

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/").setAllowedOrigins("*");
    }
    @Bean
    public WebSocketHandler myHandler() {
        return new MessageHandler();
    }
}
@Component
public class MessageHandler extends TextWebSocketHandler {
    private List
clients = new ArrayList<>();
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        clients.add(session);
        System.out.println("uri :" + session.getUri());
    }
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) {
        String payload = message.getPayload();
        clients.forEach(s -> {
            try { s.sendMessage(new TextMessage("服务器返回收到的信息," + payload)); }
            catch (Exception e) { e.printStackTrace(); }
        });
    }
}

The Spring WebSocket approach integrates easily with Spring Cloud, allowing a single service to handle both RESTful and WebSocket traffic.

Session broadcasting: A simple method where the gateway forwards a teacher's broadcast to all cluster nodes, each checking for relevant sessions. This is easy but wastes CPU when no matching sessions exist.

Consistent hashing solution (core focus): Uses a hash ring to map user IDs to specific cluster nodes, handling node up/down events via Eureka listeners and Redis‑based ring sharing. Virtual nodes improve distribution stability.

Node‑down handling removes the node and its virtual nodes from the ring; node‑up handling updates the ring and may force client reconnections to rebalance sessions.

Gateway configuration for SSL and WS forwarding:

server:
  port: 443
  ssl:
    enabled: true
    key-store: classpath:xxx.jks
    key-store-password: xxxx
    key-store-type: JKS
    key-alias: alias
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      httpclient:
        ssl:
          handshake-timeout-millis: 10000
      discovery:
        locator:
          enabled: true
      routes:
        - id: dc
          uri: lb://dc
          predicates:
            - Path=/dc/**
        - id: wecheck
          uri: lb://wecheck
          predicates:
            - Path=/wecheck/**

A custom HttpsToHttpFilter converts HTTPS requests to HTTP for proper routing.

Ribbon limitations in Finchley: Custom load‑balancing rules interfere with multi‑service routing, and the lack of a key parameter prevents direct consistent‑hash routing, leading to a two‑step client approach (HTTP to obtain target IP, then WS connection).

In conclusion, the article presents two viable distributed WebSocket strategies—session broadcast and consistent hashing—each suited to different traffic patterns, and provides practical code snippets and configuration guidance for implementation.

backenddistributed systemsJavaWebSocketgatewaySpring Cloudconsistent hashing
Code Ape Tech Column
Written by

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

0 followers
Reader feedback

How this landed with the community

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