Added websocket load test that shows that #309369 (Using websockets in multithreaded environment causes threads to block each other due to the synchronization done while scheduling a new Timeout task) is not a bug.
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1686 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
bfcd411633
commit
1445b94b8e
|
@ -21,6 +21,8 @@ jetty-7.1.0.RC1-SNAPSHOT
|
||||||
+ 308864 Update test suite to JUnit4 - Module jetty-servlets
|
+ 308864 Update test suite to JUnit4 - Module jetty-servlets
|
||||||
+ JETTY-1030 - Improve jetty.sh script
|
+ JETTY-1030 - Improve jetty.sh script
|
||||||
+ JETTY-1142 Replace Set-Cookies with same name
|
+ JETTY-1142 Replace Set-Cookies with same name
|
||||||
|
+ 309369 Using websockets in multithreaded environment causes threads to block each other
|
||||||
|
due to the synchronization done while scheduling a new Timeout task
|
||||||
|
|
||||||
jetty-7.1.0.RC0 27 April 2010
|
jetty-7.1.0.RC0 27 April 2010
|
||||||
+ 294563 Websocket client connection
|
+ 294563 Websocket client connection
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
package org.eclipse.jetty.websocket;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||||
|
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||||
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version $Revision$ $Date$
|
||||||
|
*/
|
||||||
|
public class WebSocketLoadTest
|
||||||
|
{
|
||||||
|
private static Server _server;
|
||||||
|
private static Connector _connector;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void startServer() throws Exception
|
||||||
|
{
|
||||||
|
_server = new Server();
|
||||||
|
|
||||||
|
_connector = new SelectChannelConnector();
|
||||||
|
_server.addConnector(_connector);
|
||||||
|
|
||||||
|
QueuedThreadPool threadPool = new QueuedThreadPool(200);
|
||||||
|
threadPool.setMaxStopTimeMs(1000);
|
||||||
|
_server.setThreadPool(threadPool);
|
||||||
|
|
||||||
|
WebSocketHandler wsHandler = new WebSocketHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
|
||||||
|
{
|
||||||
|
return new EchoWebSocket();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
wsHandler.setHandler(new DefaultHandler());
|
||||||
|
_server.setHandler(wsHandler);
|
||||||
|
|
||||||
|
_server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stopServer() throws Exception
|
||||||
|
{
|
||||||
|
_server.stop();
|
||||||
|
_server.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoad() throws Exception
|
||||||
|
{
|
||||||
|
int count = 500;
|
||||||
|
int iterations = 100;
|
||||||
|
|
||||||
|
ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CountDownLatch latch = new CountDownLatch(count * iterations);
|
||||||
|
WebSocketClient[] clients = new WebSocketClient[count];
|
||||||
|
for (int i = 0; i < clients.length; ++i)
|
||||||
|
{
|
||||||
|
clients[i] = new WebSocketClient("localhost", _connector.getLocalPort(), 1000, latch, iterations);
|
||||||
|
clients[i].open();
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = System.nanoTime();
|
||||||
|
for (WebSocketClient client : clients)
|
||||||
|
threadPool.execute(client);
|
||||||
|
|
||||||
|
int parallelism = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
|
||||||
|
long maxTimePerIteration = 5;
|
||||||
|
assertTrue(latch.await(iterations * (count / parallelism + 1) * maxTimePerIteration, TimeUnit.MILLISECONDS));
|
||||||
|
long end = System.nanoTime();
|
||||||
|
System.err.println("Elapsed: " + TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
|
||||||
|
|
||||||
|
for (WebSocketClient client : clients)
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
threadPool.shutdown();
|
||||||
|
assertTrue(threadPool.awaitTermination(2, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EchoWebSocket implements WebSocket
|
||||||
|
{
|
||||||
|
private volatile Outbound outbound;
|
||||||
|
|
||||||
|
public void onConnect(Outbound outbound)
|
||||||
|
{
|
||||||
|
this.outbound = outbound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMessage(byte frame, String data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
outbound.sendMessage(data);
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
outbound.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMessage(byte frame, byte[] data, int offset, int length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDisconnect()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class WebSocketClient implements Runnable
|
||||||
|
{
|
||||||
|
private final Socket socket;
|
||||||
|
private final BufferedWriter output;
|
||||||
|
private final BufferedReader input;
|
||||||
|
private final int iterations;
|
||||||
|
private final CountDownLatch latch;
|
||||||
|
|
||||||
|
public WebSocketClient(String host, int port, int readTimeout, CountDownLatch latch, int iterations) throws IOException
|
||||||
|
{
|
||||||
|
this.latch = latch;
|
||||||
|
socket = new Socket(host, port);
|
||||||
|
socket.setSoTimeout(readTimeout);
|
||||||
|
output = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "ISO-8859-1"));
|
||||||
|
input = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ISO-8859-1"));
|
||||||
|
this.iterations = iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void open() throws IOException
|
||||||
|
{
|
||||||
|
output.write("GET /test HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"Upgrade: WebSocket\r\n" +
|
||||||
|
"Connection: Upgrade\r\n" +
|
||||||
|
"\r\n");
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
String responseLine = input.readLine();
|
||||||
|
assertTrue(responseLine.startsWith("HTTP/1.1 101 Web Socket Protocol Handshake"));
|
||||||
|
// Read until we find an empty line, which signals the end of the http response
|
||||||
|
String line;
|
||||||
|
while ((line = input.readLine()) != null)
|
||||||
|
if (line.length() == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String message = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
|
||||||
|
for (int i = 0; i < iterations; ++i)
|
||||||
|
{
|
||||||
|
writeString(message);
|
||||||
|
String result = readString();
|
||||||
|
assertEquals(message, result);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeString(String message) throws IOException
|
||||||
|
{
|
||||||
|
output.write(0x00);
|
||||||
|
output.write(message);
|
||||||
|
output.write(0xFF);
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readString() throws IOException
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
int frameStart = input.read();
|
||||||
|
assertEquals(0x00, frameStart);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int read = input.read();
|
||||||
|
if (read == -1)
|
||||||
|
throw new EOFException();
|
||||||
|
char c = (char)read;
|
||||||
|
if (c == 0xFF)
|
||||||
|
break;
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue