This guide covers the complete WebSocket implementation workflow using Java, including nginx reverse proxy configuration and Spring Cloud Gateway routing setup. The implementation handles connection management, message communication, and proper proxy forwarding for WebSocket protocols.
nginx Configuration
The nginx configuration establishes an upstream pool for WebSocket connections and sets up proxy rules for routing WebSocket traffic to backend services.
upstream gateway_backend {
least_conn;
server 192.168.1.100:8080;
keepalive 1024;
keepalive_timeout 60s;
}
server {
listen 8080;
server_name websocket.example.com;
root /var/www/webapp;
index index.html;
location /api/v1 {
# REST API configuration
proxy_pass http://gateway_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /ws {
rewrite ^/ws/(.*)$ /$1 break;
proxy_pass http://gateway_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_max_temp_file_size 0;
proxy_connect_timeout 90s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
}
The critical headers for WebSocket proxying are Upgrade and Connection, which must be set to enable the protocol upgrade from HTTP to WebSocket. The keepalive settings maintain connection pools for better performance.
Spring Cloud Gateway Routing
Cnofigure the gateway to route WebSocket connections to appropriate backend services using dedicated routes.
spring:
cloud:
gateway:
routes:
- id: api-service
uri: lb://api-service
predicates:
- Path=/api/v1/**
- id: websocket-service
uri: lb:ws://websocket-service
predicates:
- Path=/ws/v2/**
Note the lb:ws:// prefix for WebSocket routes, which instructs the gateway to use WebSocket protocol for load balancing.
Java WebSocket Server Implementation
Application Bootstrap
Initialize the WebSocket server with application context access for dependency injection.
@SpringBootApplication
public class WebSocketApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(WebSocketApplication.class, args);
SocketEndpoint.registerApplicationContext(context);
}
}
WebSocket Endpoint Implementation
The following implementation provides connection lifecycle management, message handling, and proper Spring bean integration.
@ServerEndpoint(value = "/socket/{clientId}")
@Component
public class SocketEndpoint {
private static ApplicationContext appContext;
private MessageService messageService;
public static void registerApplicationContext(ApplicationContext context) {
SocketEndpoint.appContext = context;
}
private static final ConcurrentHashMap<Session, String> connectionRegistry =
new ConcurrentHashMap<>();
private static final AtomicInteger activeConnections = new AtomicInteger(0);
private Session clientSession;
private String clientId;
@OnOpen
public void handleConnectionOpen(Session session, @PathParam("clientId") String id) {
activeConnections.incrementAndGet();
this.clientSession = session;
this.clientId = id;
connectionRegistry.put(session, id);
initializeMessageService();
retrieveAndDispatchMessages(id);
log.info("Connection established. Active sessions: {}", activeConnections.get());
log.info("Client registry: {}", connectionRegistry);
}
@OnClose
public void handleConnectionClose(Session session) {
connectionRegistry.remove(session);
activeConnections.decrementAndGet();
log.info("Connection closed. Active sessions: {}", activeConnections.get());
log.info("Updated registry: {}", connectionRegistry);
}
@OnMessage
public void handleMessage(String payload, Session session) {
if (connectionRegistry.containsValue(clientId)) {
if ("ping".equals(payload)) {
sendResponse(session, "pong");
}
}
}
@OnError
public void handleError(Session session, Throwable error) {
activeConnections.decrementAndGet();
connectionRegistry.remove(clientId);
if (error instanceof EOFException) {
log.info("Client {} connection timeout via proxy, awaiting reconnect",
clientId);
} else {
log.error("WebSocket error for client {}", clientId, error);
}
}
private void initializeMessageService() {
if (appContext != null) {
this.messageService = appContext.getBean(MessageService.class);
}
}
private void sendResponse(Session session, String response) {
try {
session.getBasicRemote().sendText(response);
} catch (IOException e) {
log.error("Failed to send message", e);
}
}
private void retrieveAndDispatchMessages(String clientId) {
// Implementation for fetching pending messages
// and sending to connected client
}
}
Client Connection Endpoint
Clients establish WebSocket connections using the following endpoint pattern:
ws://localhost:8080/ws/v2/socket/client-123
or with SSL:
wss://localhost:8080/ws/v2/socket/client-123
Key Implementation Notes
This implementation addresses several critical aspects of WebSocket development. The static application context reference enables Spring dependency injection within the WebSocket endpoint, which is not natively supported by the Java WebSocket API. The connection registry maintains a mapping between sessions and client identifiers for targeted message delivery. Error handling specifically addresess proxy timeout scenarios, allowing clients to implement reconnection logic.
The nginx proxy timeout settings (90 seconds in this configuration) should be adjusted based on expected connection durations and heartbeat intervals to prevent premature connection termination.