google-site-verification: google61fe8ba583a51912.html
Load Testing WebSocket Endpoints in Java

Load testing WebSocket endpoints requires specialized tools and approaches due to the persistent, bidirectional nature of WebSocket connections. Here's a comprehensive guide to load testing WebSocket endpoints using various Java tools and frameworks.

Tools and Libraries

Dependencies Setup

<properties> <jetty.version>11.0.17</jetty.version> <jmeter.version>5.6.2</jmeter.version> <tcpmon.version>1.0.0</tcpmon.version> </properties> <dependencies> <!-- Jetty WebSocket Client --> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-jetty-client</artifactId> <version>${jetty.version}</version> </dependency> <!-- Jetty WebSocket Server --> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-jetty-server</artifactId> <version>${jetty.version}</version> </dependency> <!-- Java WebSocket API --> <dependency> <groupId>jakarta.websocket</groupId> <artifactId>jakarta.websocket-client-api</artifactId> <version>2.1.0</version> </dependency> <!-- Apache HttpClient for REST calls --> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency> <!-- Metrics --> <dependency> <groupId>io.dropwizard.metrics</groupId> <artifactId>metrics-core</artifactId> <version>4.2.18</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.3</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.9</version> </dependency> <!-- Test --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> </dependencies>

Example WebSocket Server for Testing

Example 1: Simple WebSocket Server

import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; import jakarta.websocket.*; import jakarta.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @ServerEndpoint("/chat") public class ChatWebSocketServer { private static final Map<String, Session> sessions = new ConcurrentHashMap<>(); private static final AtomicInteger connectionCount = new AtomicInteger(0); private static final AtomicInteger messageCount = new AtomicInteger(0); @OnOpen public void onOpen(Session session) { sessions.put(session.getId(), session); int count = connectionCount.incrementAndGet(); System.out.println("Client connected: " + session.getId() + ", Total connections: " + count); // Send welcome message sendMessage(session, "Welcome! You are client #" + count); } @OnMessage public void onMessage(String message, Session session) { int count = messageCount.incrementAndGet(); System.out.println("Received message from " + session.getId() + ": " + message); System.out.println("Total messages processed: " + count); // Echo the message back sendMessage(session, "Echo: " + message); // Broadcast to all connected clients broadcast("Client " + session.getId() + " said: " + message); } @OnClose public void onClose(Session session, CloseReason closeReason) { sessions.remove(session.getId()); int count = connectionCount.decrementAndGet(); System.out.println("Client disconnected: " + session.getId() + ", Reason: " + closeReason.getReasonPhrase() + ", Remaining connections: " + count); } @OnError public void onError(Session session, Throwable throwable) { System.err.println("Error for client " + session.getId() + ": " + throwable.getMessage()); } private void sendMessage(Session session, String message) { try { session.getBasicRemote().sendText(message); } catch (IOException e) { System.err.println("Failed to send message to " + session.getId() + ": " + e.getMessage()); } } private void broadcast(String message) { sessions.values().forEach(session -> sendMessage(session, message)); } public static int getConnectionCount() { return connectionCount.get(); } public static int getMessageCount() { return messageCount.get(); } public static void resetCounters() { connectionCount.set(0); messageCount.set(0); } // Server setup public static Server createServer(int port) { Server server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setPort(port); server.addConnector(connector); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); // Configure WebSocket JettyWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> { wsContainer.setMaxTextMessageBufferSize(65536); wsContainer.setIdleTimeout(300000L); // 5 minutes }); return server; } }

Custom WebSocket Load Testing Framework

Example 2: WebSocket Load Tester

import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; import jakarta.websocket.*; import java.net.URI; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; public class WebSocketLoadTester { private final String serverUrl; private final int totalConnections; private final int messagesPerConnection; private final long messageIntervalMs; private final ExecutorService executor; private final AtomicInteger successfulConnections = new AtomicInteger(0); private final AtomicInteger failedConnections = new AtomicInteger(0); private final AtomicLong totalMessagesSent = new AtomicLong(0); private final AtomicLong totalMessagesReceived = new AtomicLong(0); private final AtomicLong totalResponseTime = new AtomicLong(0); public WebSocketLoadTester(String serverUrl, int totalConnections, int messagesPerConnection, long messageIntervalMs) { this.serverUrl = serverUrl; this.totalConnections = totalConnections; this.messagesPerConnection = messagesPerConnection; this.messageIntervalMs = messageIntervalMs; this.executor = Executors.newFixedThreadPool(totalConnections); } public LoadTestResult runTest() throws Exception { System.out.println("Starting WebSocket load test..."); System.out.println("Connections: " + totalConnections + ", Messages per connection: " + messagesPerConnection); CountDownLatch connectionLatch = new CountDownLatch(totalConnections); CountDownLatch completionLatch = new CountDownLatch(totalConnections); long startTime = System.currentTimeMillis(); // Create and start all client connections for (int i = 0; i < totalConnections; i++) { final int clientId = i; executor.submit(() -> { try { runClient(clientId, connectionLatch, completionLatch); } catch (Exception e) { System.err.println("Client " + clientId + " failed: " + e.getMessage()); failedConnections.incrementAndGet(); connectionLatch.countDown(); completionLatch.countDown(); } }); } // Wait for all connections to be established connectionLatch.await(2, TimeUnit.MINUTES); // Wait for all clients to complete their message cycles completionLatch.await(10, TimeUnit.MINUTES); long endTime = System.currentTimeMillis(); long totalDuration = endTime - startTime; executor.shutdown(); return new LoadTestResult( successfulConnections.get(), failedConnections.get(), totalMessagesSent.get(), totalMessagesReceived.get(), totalResponseTime.get(), totalDuration ); } private void runClient(int clientId, CountDownLatch connectionLatch, CountDownLatch completionLatch) throws Exception { WebSocketClient client = new WebSocketClient(); client.start(); try { LoadTestClientSocket socket = new LoadTestClientSocket(clientId); ClientUpgradeRequest request = new ClientUpgradeRequest(); URI echoUri = new URI(serverUrl); client.connect(socket, echoUri, request); // Wait for connection if (socket.awaitConnection(30, TimeUnit.SECONDS)) { successfulConnections.incrementAndGet(); connectionLatch.countDown(); // Send messages for (int i = 0; i < messagesPerConnection; i++) { String message = String.format("Client-%d-Message-%d", clientId, i); long sendTime = System.currentTimeMillis(); socket.sendMessage(message, sendTime); totalMessagesSent.incrementAndGet(); // Wait before sending next message if (messageIntervalMs > 0) { Thread.sleep(messageIntervalMs); } } // Wait for all responses socket.awaitCompletion(1, TimeUnit.MINUTES); totalMessagesReceived.addAndGet(socket.getMessagesReceived()); totalResponseTime.addAndGet(socket.getTotalResponseTime()); } else { failedConnections.incrementAndGet(); connectionLatch.countDown(); } } finally { completionLatch.countDown(); client.stop(); } } @ClientEndpoint public static class LoadTestClientSocket { private final int clientId; private Session session; private final CountDownLatch connectionLatch = new CountDownLatch(1); private final CountDownLatch completionLatch; private final AtomicInteger messagesReceived = new AtomicInteger(0); private final AtomicLong totalResponseTime = new AtomicLong(0); public LoadTestClientSocket(int clientId) { this.clientId = clientId; this.completionLatch = new CountDownLatch(1); } @OnOpen public void onOpen(Session session) { this.session = session; System.out.println("Client " + clientId + " connected"); connectionLatch.countDown(); } @OnMessage public void onMessage(String message, Session session) { long receiveTime = System.currentTimeMillis(); // Extract send time from message if possible, or use other tracking mechanism // For this example, we'll just count the message messagesReceived.incrementAndGet(); // Simple response time calculation (would need message tracking in real scenario) totalResponseTime.addAndGet(10); // Placeholder if (message.contains("Final")) { completionLatch.countDown(); } } @OnClose public void onClose(Session session, CloseReason closeReason) { System.out.println("Client " + clientId + " disconnected: " + closeReason.getReasonPhrase()); completionLatch.countDown(); } @OnError public void onError(Session session, Throwable throwable) { System.err.println("Client " + clientId + " error: " + throwable.getMessage()); } public boolean awaitConnection(long timeout, TimeUnit unit) throws InterruptedException { return connectionLatch.await(timeout, unit); } public void awaitCompletion(long timeout, TimeUnit unit) throws InterruptedException { completionLatch.await(timeout, unit); } public void sendMessage(String message, long sendTime) { try { String trackedMessage = message + "|" + sendTime; session.getBasicRemote().sendText(trackedMessage); } catch (Exception e) { System.err.println("Client " + clientId + " failed to send message: " + e.getMessage()); } } public int getMessagesReceived() { return messagesReceived.get(); } public long getTotalResponseTime() { return totalResponseTime.get(); } } public static class LoadTestResult { public final int successfulConnections; public final int failedConnections; public final long totalMessagesSent; public final long totalMessagesReceived; public final long totalResponseTime; public final long totalDurationMs; public LoadTestResult(int successfulConnections, int failedConnections, long totalMessagesSent, long totalMessagesReceived, long totalResponseTime, long totalDurationMs) { this.successfulConnections = successfulConnections; this.failedConnections = failedConnections; this.totalMessagesSent = totalMessagesSent; this.totalMessagesReceived = totalMessagesReceived; this.totalResponseTime = totalResponseTime; this.totalDurationMs = totalDurationMs; } public void printSummary() { System.out.println("\n=== LOAD TEST SUMMARY ==="); System.out.println("Successful connections: " + successfulConnections); System.out.println("Failed connections: " + failedConnections); System.out.println("Total messages sent: " + totalMessagesSent); System.out.println("Total messages received: " + totalMessagesReceived); System.out.println("Message success rate: " + String.format("%.2f%%", (double) totalMessagesReceived / totalMessagesSent * 100)); System.out.println("Average response time: " + (totalMessagesReceived > 0 ? totalResponseTime / totalMessagesReceived : 0) + "ms"); System.out.println("Total duration: " + totalDurationMs + "ms"); System.out.println("Messages per second: " + (totalDurationMs > 0 ? (totalMessagesSent * 1000) / totalDurationMs : 0)); } } public static void main(String[] args) throws Exception { String serverUrl = "ws://localhost:8080/chat"; WebSocketLoadTester tester = new WebSocketLoadTester(serverUrl, 100, 10, 100); LoadTestResult result = tester.runTest(); result.printSummary(); } }

Advanced Load Testing with Metrics

Example 3: Metrics-Based Load Tester

import com.codahale.metrics.*; import org.eclipse.jetty.websocket.client.WebSocketClient; import java.net.URI; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class MetricsWebSocketLoadTester { private final MetricRegistry metrics = new MetricRegistry(); private final String serverUrl; private final int totalUsers; private final int rampUpSeconds; private final int durationSeconds; private final int messagesPerSecond; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10); private final ExecutorService userExecutor = Executors.newCachedThreadPool(); // Metrics private final Meter messagesSent = metrics.meter("messages-sent"); private final Meter messagesReceived = metrics.meter("messages-received"); private final Meter connectionErrors = metrics.meter("connection-errors"); private final Timer responseTime = metrics.timer("response-time"); private final Counter activeConnections = metrics.counter("active-connections"); private final Histogram messageSize = metrics.histogram("message-size"); public MetricsWebSocketLoadTester(String serverUrl, int totalUsers, int rampUpSeconds, int durationSeconds, int messagesPerSecond) { this.serverUrl = serverUrl; this.totalUsers = totalUsers; this.rampUpSeconds = rampUpSeconds; this.durationSeconds = durationSeconds; this.messagesPerSecond = messagesPerSecond; } public void runTest() throws Exception { System.out.println("Starting metrics-based WebSocket load test..."); // Start metrics reporter startMetricsReporter(); // Ramp up users rampUpUsers(); // Run at steady state Thread.sleep(durationSeconds * 1000L); // Stop test stopTest(); // Print final report printFinalReport(); } private void rampUpUsers() { int usersPerSecond = totalUsers / rampUpSeconds; for (int i = 0; i < totalUsers; i++) { final int userId = i; userExecutor.submit(() -> startUser(userId)); if (i % usersPerSecond == 0 && i > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } private void startUser(int userId) { try { WebSocketClient client = new WebSocketClient(); client.start(); MetricsClientSocket socket = new MetricsClientSocket(userId, metrics); client.connect(socket, new URI(serverUrl)); activeConnections.inc(); // Send messages at specified rate ScheduledFuture<?> messageTask = scheduler.scheduleAtFixedRate(() -> { if (socket.isConnected()) { String message = generateTestMessage(userId); messageSize.update(message.length()); Timer.Context context = responseTime.time(); socket.sendMessage(message); messagesSent.mark(); // In real implementation, you'd track the message and complete the timer when response received context.close(); } }, 0, 1000 / messagesPerSecond, TimeUnit.MILLISECONDS); // Stop sending after test duration scheduler.schedule(() -> { messageTask.cancel(true); try { client.stop(); } catch (Exception e) { System.err.println("Error stopping client for user " + userId + ": " + e.getMessage()); } activeConnections.dec(); }, durationSeconds + rampUpSeconds, TimeUnit.SECONDS); } catch (Exception e) { connectionErrors.mark(); System.err.println("Failed to start user " + userId + ": " + e.getMessage()); } } private String generateTestMessage(int userId) { return String.format("{\"userId\": %d, \"timestamp\": %d, \"payload\": \"%s\"}", userId, System.currentTimeMillis(), "x".repeat(100)); // 100-byte payload } private void startMetricsReporter() { ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(10, TimeUnit.SECONDS); // Stop reporter when test ends scheduler.schedule(reporter::stop, durationSeconds + rampUpSeconds + 5, TimeUnit.SECONDS); } private void stopTest() { userExecutor.shutdown(); scheduler.shutdown(); try { if (!userExecutor.awaitTermination(30, TimeUnit.SECONDS)) { userExecutor.shutdownNow(); } if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private void printFinalReport() { System.out.println("\n=== FINAL LOAD TEST REPORT ==="); System.out.println("Total messages sent: " + messagesSent.getCount()); System.out.println("Total messages received: " + messagesReceived.getCount()); System.out.println("Connection errors: " + connectionErrors.getCount()); System.out.println("Average response time: " + responseTime.getSnapshot().getMean() + "ms"); System.out.println("95th percentile response time: " + responseTime.getSnapshot().get95thPercentile() + "ms"); } @ClientEndpoint public static class MetricsClientSocket { private final int userId; private final MetricRegistry metrics; private Session session; private volatile boolean connected = false; public MetricsClientSocket(int userId, MetricRegistry metrics) { this.userId = userId; this.metrics = metrics; } @OnOpen public void onOpen(Session session) { this.session = session; this.connected = true; System.out.println("User " + userId + " connected successfully"); } @OnMessage public void onMessage(String message) { metrics.meter("messages-received").mark(); // Process message and update metrics as needed } @OnClose public void onClose(Session session, CloseReason closeReason) { this.connected = false; System.out.println("User " + userId + " disconnected: " + closeReason.getReasonPhrase()); } @OnError public void onError(Session session, Throwable throwable) { metrics.meter("connection-errors").mark(); System.err.println("User " + userId + " error: " + throwable.getMessage()); } public void sendMessage(String message) { if (connected && session != null) { try { session.getBasicRemote().sendText(message); } catch (Exception e) { System.err.println("Failed to send message for user " + userId + ": " + e.getMessage()); } } } public boolean isConnected() { return connected; } } public static void main(String[] args) throws Exception { MetricsWebSocketLoadTester tester = new MetricsWebSocketLoadTester( "ws://localhost:8080/chat", 500, // total users 30, // ramp up over 30 seconds 120, // run for 2 minutes 5 // 5 messages per second per user ); tester.runTest(); } }

Integration with JMeter

Example 4: JMeter WebSocket Test Plan Generator

import org.apache.jmeter.config.Arguments; import org.apache.jmeter.protocol.http.sampler.WebSocketSampler; import org.apache.jmeter.protocol.http.control.HeaderManager; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestPlan; import org.apache.jorphan.collections.HashTree; import java.io.FileOutputStream; import java.util.Properties; public class JMeterWebSocketTestGenerator { public static void generateWebSocketTestPlan(String outputPath) throws Exception { HashTree testPlanTree = new HashTree(); // Create Test Plan TestPlan testPlan = new TestPlan("WebSocket Load Test"); testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName()); testPlan.setProperty(TestElement.GUI_CLASS, "TestPlanGui"); testPlanTree.add(testPlan); // Create Thread Group org.apache.jmeter.threads.ThreadGroup threadGroup = new org.apache.jmeter.threads.ThreadGroup(); threadGroup.setName("WebSocket Users"); threadGroup.setNumThreads(100); threadGroup.setRampUp(30); threadGroup.setDuration(300); threadGroup.setProperty(TestElement.TEST_CLASS, org.apache.jmeter.threads.ThreadGroup.class.getName()); threadGroup.setProperty(TestElement.GUI_CLASS, "ThreadGroupGui"); HashTree threadGroupHash = testPlanTree.add(testPlan, threadGroup); // Create WebSocket Sampler WebSocketSampler webSocketSampler = new WebSocketSampler(); webSocketSampler.setName("WebSocket Echo Test"); webSocketSampler.setDomain("localhost"); webSocketSampler.setPort("8080"); webSocketSampler.setPath("/chat"); webSocketSampler.setProtocol("ws"); webSocketSampler.setImplementation("RFC6455"); webSocketSampler.setResponseTimeout("5000"); webSocketSampler.setConnectionTimeout("10000"); webSocketSampler.setStreamingConnection(true); webSocketSampler.setRequestData("Hello from JMeter - ${__threadNum}"); // Add Header Manager if needed HeaderManager headerManager = new HeaderManager(); headerManager.setName("WebSocket Headers"); headerManager.addHeader("Origin", "http://localhost:8080"); threadGroupHash.add(webSocketSampler); threadGroupHash.add(headerManager); // Add listeners addListeners(threadGroupHash); // Save test plan saveTestPlan(testPlanTree, outputPath); } private static void addListeners(HashTree threadGroupHash) { // Add Summary Report org.apache.jmeter.reporters.ResultCollector summaryReport = new org.apache.jmeter.reporters.ResultCollector(); summaryReport.setName("Summary Report"); summaryReport.setProperty(TestElement.TEST_CLASS, org.apache.jmeter.reporters.ResultCollector.class.getName()); summaryReport.setProperty(TestElement.GUI_CLASS, "SummaryReport"); // Add View Results Tree org.apache.jmeter.visualizers.ViewResultsFullVisualizer resultsTree = new org.apache.jmeter.visualizers.ViewResultsFullVisualizer(); resultsTree.setName("View Results Tree"); resultsTree.setProperty(TestElement.TEST_CLASS, org.apache.jmeter.visualizers.ViewResultsFullVisualizer.class.getName()); resultsTree.setProperty(TestElement.GUI_CLASS, "ViewResultsFullVisualizer"); threadGroupHash.add(summaryReport); threadGroupHash.add(resultsTree); } private static void saveTestPlan(HashTree testPlanTree, String outputPath) throws Exception { Properties props = new Properties(); props.setProperty("jmeter.save.saveservice.output_format", "xml"); props.setProperty("jmeter.save.saveservice.assertions", "true"); props.setProperty("jmeter.save.saveservice.bytes", "true"); org.apache.jmeter.save.SaveService.saveTree(testPlanTree, new FileOutputStream(outputPath), props); System.out.println("JMeter test plan saved to: " + outputPath); } public static void main(String[] args) throws Exception { generateWebSocketTestPlan("websocket_load_test.jmx"); } }

Performance Monitoring

Example 5: Real-time Performance Monitor

import java.lang.management.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class WebSocketPerformanceMonitor { private final ScheduledExecutorService monitorExecutor = Executors.newScheduledThreadPool(1); private final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); private final OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); private long lastGcCount = 0; private long lastGcTime = 0; public void startMonitoring() { System.out.println("Starting performance monitoring..."); System.out.println("Time\tCPU%\tUsedMem\tMaxMem\tThreads\tActiveConn"); monitorExecutor.scheduleAtFixedRate(() -> { try { printMetrics(); } catch (Exception e) { System.err.println("Error in performance monitoring: " + e.getMessage()); } }, 0, 5, TimeUnit.SECONDS); } public void stopMonitoring() { monitorExecutor.shutdown(); try { if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) { monitorExecutor.shutdownNow(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private void printMetrics() { long currentTime = System.currentTimeMillis(); // Memory usage long usedMemory = memoryBean.getHeapMemoryUsage().getUsed() / (1024 * 1024); long maxMemory = memoryBean.getHeapMemoryUsage().getMax() / (1024 * 1024); // Thread count int threadCount = threadBean.getThreadCount(); // GC statistics long gcCount = 0; long gcTime = 0; for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { gcCount += gc.getCollectionCount(); gcTime += gc.getCollectionTime(); } long gcCountDelta = gcCount - lastGcCount; long gcTimeDelta = gcTime - lastGcTime; lastGcCount = gcCount; lastGcTime = gcTime; // Active connections (you would get this from your WebSocket server) int activeConnections = ChatWebSocketServer.getConnectionCount(); System.out.printf("%tT\t%.1f\t%dM\t%dM\t%d\t%d\t(GC: %d/%dms)%n", currentTime, getProcessCpuLoad(), usedMemory, maxMemory, threadCount, activeConnections, gcCountDelta, gcTimeDelta); } private double getProcessCpuLoad() { if (osBean instanceof com.sun.management.OperatingSystemMXBean) { return ((com.sun.management.OperatingSystemMXBean) osBean).getProcessCpuLoad() * 100; } return 0.0; } public static void main(String[] args) throws Exception { WebSocketPerformanceMonitor monitor = new WebSocketPerformanceMonitor(); monitor.startMonitoring(); // Run for 5 minutes Thread.sleep(300000); monitor.stopMonitoring(); } }

Best Practices for WebSocket Load Testing

1. Test Scenarios

public class WebSocketTestScenarios { public enum TestScenario { // Low concurrency, high message rate CHAT_ROOM(50, 1000, 10, "Chat room simulation"), // High concurrency, low message rate NOTIFICATIONS(1000, 10, 100, "Notification service"), // Mixed workload TRADING_PLATFORM(500, 100, 50, "Trading platform"), // Stress test STRESS_TEST(5000, 5, 200, "Maximum capacity test"); public final int connections; public final int messagesPerMinute; public final int messageSizeBytes; public final String description; TestScenario(int connections, int messagesPerMinute, int messageSizeBytes, String description) { this.connections = connections; this.messagesPerMinute = messagesPerMinute; this.messageSizeBytes = messageSizeBytes; this.description = description; } } public static void runScenario(TestScenario scenario, String serverUrl) throws Exception { System.out.println("Running scenario: " + scenario.description); WebSocketLoadTester tester = new WebSocketLoadTester( serverUrl, scenario.connections, scenario.messagesPerMinute, 60000 / scenario.messagesPerMinute // Convert to interval ); LoadTestResult result = tester.runTest(); result.printSummary(); } }

2. Key Metrics to Monitor

  • Connection establishment time
  • Message round-trip time
  • Connection stability and drop rate
  • Memory usage and GC activity
  • CPU utilization
  • Network I/O

Conclusion

WebSocket load testing requires specialized approaches due to the persistent nature of connections. Key strategies include:

  1. Use Appropriate Tools: Jetty WebSocket client for Java-based testing, JMeter for complex scenarios
  2. Monitor Key Metrics: Connection count, message throughput, response times, resource usage
  3. Simulate Realistic Workloads: Consider message rates, connection patterns, and payload sizes
  4. Implement Proper Cleanup: Ensure connections are properly closed to avoid resource leaks
  5. Use Metrics Libraries: For comprehensive performance monitoring and reporting

The provided examples give you a solid foundation for building comprehensive WebSocket load testing solutions tailored to your specific requirements.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper