I have been doing some more thinking on why my CometD load-generator for Jetty is failing. Maybe at the end of the day load testing between a remote client and a server is a red herring. Perhaps the correct approach is to run the load-generator on the server and if this works test remote connection responses with some other mechanism such as Apache Benchmark or JMeter.
I am seeing various problems testing the server _on_ the server when I get up to about the 500 client point. I continued to investigate the issues - the first thing I did was to upgrade to Jetty 6.1.19 which includes the cometd-api-1.0.beta9.jar
Note this doesn't solve my problem, although I think I am seeing less JVM bug related warnings now and I also seem to be encountering less java.lang.IllegalStateException: State==HEADER exceptions while running the load-generator.
Something I didn't mention in my original mail (I wanted to tackle one issue at a time) is that if I try to run more than around 500 clients I was also encountering another exception reported from Jetty:
WARN: EXCEPTION
java.io.IOException: Too many open files
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:163)
at org.mortbay.jetty.nio.SelectChannelConnector$1.acceptChannel(SelectChannelConnector.java:75)
at org.mortbay.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:565)
at org.mortbay.io.nio.SelectorManager.doSelect(SelectorManager.java:192)
at org.mortbay.jetty.nio.SelectChannelConnector.accept(SelectChannelConnector.java:124)
at org.mortbay.jetty.AbstractConnector$Acceptor.run(AbstractConnector.java:706)And about half the time from my load generator:
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:520
WARN: lafuria.TestBayeuxLoadGenerator$1@7041a12f {"failure":"java.net.SocketException: Too many open files","successful":false,"exception":"java.net.SocketException: Too many open files"}
WARN: CONNECTION FAILED on HttpExchange@1440914329=POST//localhost:8008/lafuria/cometd/publish#1
java.net.SocketException: Too many open files
at sun.nio.ch.Net.socket0(Native Method)
at sun.nio.ch.Net.socket(Net.java:112)
at sun.nio.ch.SocketChannelImpl.<init>(SocketChannelImpl.java:102)
at sun.nio.ch.SelectorProviderImpl.openSocketChannel(SelectorProviderImpl.java:55)
at java.nio.channels.SocketChannel.open(SocketChannel.java:122)
at org.mortbay.jetty.client.SelectConnector.startConnection(SelectConnector.java:67)
at org.mortbay.jetty.client.HttpDestination.startNewConnection(HttpDestination.java:243)
at org.mortbay.jetty.client.HttpDestination.doSend(HttpDestination.java:474)
at org.mortbay.jetty.client.HttpDestination.send(HttpDestination.java:420)
at org.mortbay.jetty.client.HttpClient.send(HttpClient.java:142)
at lafuria.BayeuxClient.send(BayeuxClient.java:1340)
at lafuria.BayeuxClient$Connect.onResponseComplete(BayeuxClient.java:957)
at org.mortbay.jetty.client.HttpExchange$Listener.onResponseComplete(HttpExchange.java:585)
at org.mortbay.jetty.client.HttpExchange.setStatus(HttpExchange.java:179)
at org.mortbay.jetty.client.HttpConnection$Handler.messageComplete(HttpConnection.java:543)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:752)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.client.HttpConnection.handle(HttpConnection.java:269)I tried increasing the number of open file descriptors using ulimit -n8192 and this seems to stop the Jetty exceptions but the load generator exhibits strange behaviour. Running it with a large number of clients such as 700 - it subscribes all the clients and after about a second usually starts firing out the above exception one after another, however occassionally it seems to run without exceptions and I can then go on to run the 'pusher' and conclude my testing. It's really this intermittent behaviour that's bothering me.
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:520)
Another thing I tried was to increase the QueueThreadPool maxThreads value but this seemed to make no difference, neither did allocating more memory to the load-generator.
The only success I've had is upping the ulimit using ulimit -n8192 but feel (although I don't know) that it shouldn't have to be so high.
I've taken a look at the jetty.xml file but didn't see obvious values to increase to allow it to cope with higher loads.
I continue to feel that there is something wrong with my load-generator code. I don't think that I am trying to do something too intensive; pushing a message out to 1000 subscribed clients, getting them to reply as quickly as possible and measure the results. Although I do wonder whether it would it be better to split into separate channels.
Also am I sending this to the right group. Possibly better to send cometd-dev group?
Any ideas or thoughts on this are very much appreciated.
Thanks
Mark2009/7/2 Mark <mark...@gmail.com>
Hi all.
I hope this is the best place to send my query.
I'm thinking of writing a blog post on getting started with cometd and Jetty. One on the important aspects I want to touch on is load testing and scaling and so am writing a simple application complete with load generator to illustrate my points.
My cometd based application is very simple and consists of a 'pusher' which pushes out a small amount of data to all clients, and a service which logs the client's immediate responses. The idea is that the round trip is measured.
So I have the following channels :
/testPusher
/service/testresponse
At the moment I am experiencing larger than expected 'lags' load testing from my laptop locally on various servers located in different locations:
Running a simulation locally I see all 100 clients complete their 'round-trips' in around 300ms. But running a simulation of the same 100 clients from my laptop to on an EC3 instance or dedicated server I see lags between the first client's response and last of up to 10,000ms! I don't think this can just be down to network lag. I suspect there maybe something wrong with my load generator. I have tried removing the .startBatch and .endBatch from around the .publish command in the load generator but am not seeing any real difference.
Any insights would be greatly appreciated.
I'm also experiencing the following error when I try to run around 500 clients:
2009-07-02 15:23:00.748::WARN: lafuria.TestBayeuxLoadGenerator$1@25b88c {"exception":"org.mortbay.jetty.EofException","successful":false,"failure":"org.mortbay.jetty.EofException"}
2009-07-02 15:23:00.749::WARN: handle failed
java.lang.IllegalStateException: State==HEADER
at org.mortbay.jetty.HttpGenerator.flush(HttpGenerator.java:683)
at org.mortbay.jetty.client.HttpConnection.handle(HttpConnection.java:241)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:520)
Thanks in advance
Mark
-----------------------------------------------------------------------------------
The code:
My application consists of two files TestPusher:
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
import org.cometd.*;
import org.mortbay.log.Log;
public class TestPusher extends HttpServlet
{
public void doFilter(ServletRequest request,ServletResponse response)
throws IOException, ServletException
{
Bayeux bayeux=(Bayeux)getServletContext().getAttribute(Bayeux.ATTRIBUTE);
Channel channel = bayeux.getChannel("/testPusher", true);
Client client = bayeux.newClient("client");
Map<String,Object> message = new HashMap<String,Object>();
message.put("payload", "ok");
channel.publish(client ,message, null);
Log.info("TestPusher fired.");
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
doFilter(request,response);
}
}
and
TestService:
import java.util.Map;
import org.cometd.Bayeux;
import org.cometd.Client;
import org.mortbay.cometd.BayeuxService;
import org.mortbay.log.Log;
public class TestService extends BayeuxService
{
static int successes = 0;
public TestService(Bayeux bayeux)
{
super(bayeux, "/service/testresponse");
Log.info("TestService");
subscribe("/service/testresponse", "testResponse");
}
public void testResponse(Client source, String channel, Map<String, Object> data, String messageId)
{
synchronized (this)
{
Log.info("received : "+(++successes)+" - clientNo : "+data.get("clientNo"));
}
}
}
I have also created a TestBayeuxLoadGenerator taking inspiration from the excellent BayeuxLoadGenerator.java written to load test Jetty's demo chat application. I am trying to keep things to the every minimum so they are understandable to the beginner.
TestBayeuxLoadGenerator:
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.security.SecureRandom;
import java.util.ArrayList;
//import java.util.Timer;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.*;
import java.sql.Timestamp;
import org.cometd.Bayeux;
import org.cometd.Client;
import org.cometd.Message;
import org.cometd.MessageListener;
import org.mortbay.cometd.AbstractBayeux;
import org.mortbay.jetty.client.Address;
import org.mortbay.jetty.client.HttpClient;
import org.mortbay.thread.QueuedThreadPool;
import org.mortbay.util.ajax.JSON;
public class TestBayeuxLoadGenerator
{
SecureRandom _random= new SecureRandom();
HttpClient http;
Address address;
ArrayList<BayeuxClient> clients=new ArrayList<BayeuxClient>();
AtomicInteger _subscribed = new AtomicInteger();
String host = "localhost";
String uri = "/lafuria/cometd";
int port = 8080;
int nclients = 100;
AtomicInteger clientNo = new AtomicInteger();
public TestBayeuxLoadGenerator() throws Exception
{
http=new HttpClient();
http.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
// http.setConnectorType(HttpClient.CONNECTOR_SOCKET);
http.setMaxConnectionsPerAddress(40000);
QueuedThreadPool pool = new QueuedThreadPool();
pool.setMaxThreads(500);
pool.setDaemon(true);
http.setThreadPool(pool);
http.start();
}
public void generateLoad() throws Exception
{
System.out.println("----------------------------------------------------------------------------------");
System.out.println("TestBayeuxLoadGenerator (build 2) press return to go with defaults. CTRL+C to Quit");
System.out.println("----------------------------------------------------------------------------------");
LineNumberReader in = new LineNumberReader(new InputStreamReader(System.in));
System.out.print("server["+host+"]: ");
String t = in.readLine().trim();
if (t.length()==0)
t=host;
host=t;
System.out.print("port["+port+"]: ");
t = in.readLine().trim();
if (t.length()==0)
t=port+"";
port = Integer.parseInt(t);
System.out.print("clients["+nclients+"]: ");
t = in.readLine().trim();
if (t.length()==0)
t=nclients+"";
nclients = Integer.parseInt(t);
address=new Address(host,port);
while (clients.size()<nclients)
{
int u=clients.size();
BayeuxClient client = new BayeuxClient(http,address,uri)
{
public void deliver(Client from, Message message)
{
if (Bayeux.META_SUBSCRIBE.equals(message.get(Bayeux.CHANNEL_FIELD)) &&
((Boolean)message.get(Bayeux.SUCCESSFUL_FIELD)).booleanValue())
{
_subscribed.incrementAndGet();
}
super.deliver(from,message);
}
};
//client.addExtension(new TimesyncClientExtension());
MessageListener listener = new MessageListener()
{
public void deliver(Client fromClient, Client toClient, Message msg)
{
Object data=(Object)msg.get(AbstractBayeux.DATA_FIELD);
if (data!=null)
{
BayeuxClient c = clients.get(clientNo.get());
Object pmsg=new JSON.Literal("{\"clientNo\":\""+clientNo.getAndIncrement()+"\",\"data\":\""+data.toString()+"\"}");
clientNo.compareAndSet(nclients, 0);
String id=""+System.currentTimeMillis();
Date myDate = new Date();
Timestamp myTs = new Timestamp(myDate.getTime());
System.out.println(myTs+" sending "+pmsg);
// BayeuxClient.startBatch() / endBatch()
// Start a batch of messages. Messages will not be delivered remotely until the corresponding endBatch is called.
// Batches may be nested and messages are only sent once all batches are ended.
// http://mortbay.org/jetty/cometd/apidocs/org/cometd/Client.html#startBatch()
c.startBatch();
c.publish("/service/testresponse",pmsg,id);
c.endBatch();
}
}
};
client.addListener(listener);
client.start();
System.out.println("Adding client "+u);
clients.add(client);
client.startBatch();
System.out.println("Subscribing client "+u);
client.subscribe("/testPusher");
client.subscribe("/service/testresponse");
client.endBatch();
}
while(true){}
}
public static void main(String[] args) throws Exception
{
TestBayeuxLoadGenerator gen = new TestBayeuxLoadGenerator();
gen.generateLoad();
}
}
On Wed, Jul 8, 2009 at 12:32, Mark<mark...@gmail.com> wrote:
> I'm posting this to cometd-users as well as jetty-user as I suspect it may
> be more a cometd issue than a Jetty issue.
[snip]
> lafuria.TestBayeuxLoadGenerator$1@f9e6e5
> {"exception":"java.lang.IllegalStateException:
> State==HEADER","successful":false,"failure":"java.lang.IllegalStateException:
> State==HEADER"}
Make sure that the timeouts for the client and the server are very
different, with the server's longer than the client.
Client timeout could be 5 seconds, server timeout 60 seconds.
Simon
--
http://bordet.blogspot.com
---
Finally, no matter how good the architecture and design are,
to deliver bug-free software with optimal performance and reliability,
the implementation technique must be flawless. Victoria Livschitz
Make sure that the timeouts for the client and the server are very
different, with the server's longer than the client.
Client timeout could be 5 seconds, server timeout 60 seconds.
On Wed, Jul 22, 2009 at 19:05, Mark<mark...@gmail.com> wrote:
>> Make sure that the timeouts for the client and the server are very
>> different, with the server's longer than the client.
>>
>> Client timeout could be 5 seconds, server timeout 60 seconds.
>
> OK. I have changed a few parameters:
>
> Client :
>
> jetty.xml
>
> Increased maxThreads to 1200 (from 500)
> Reduced maxIdleTime to 5000 (from 30000)
If you modified these parameter in jetty.xml, then you changed the
server and not the client.
What I meant was that you should do:
HttpClient httpClient = ...;
httpClient.setIdleTimeout(5000);
This is the httpClient that you pass to BayeuxClient.
I spent quite a while as well looking at these errors, and tracing
them with wireshark.
In my case it turned out to be a race between the client and the
server timeout during load testing: the client was issuing a request
in the same moment the server was closing the connection for a
timeout.
By setting the client timeout much less than the server, all errors of
this kind (connection reset by peer) went away and I could run load
tests for many hours with no errors at a decent rate (where decent
meant thousand requests/second).
Try what above and let us know.
On Thu, Jul 23, 2009 at 17:23, Mark<mark...@gmail.com> wrote:
> Hi Simone,
>
> Thanks again for your help. I've been stuck on this for a while so I really
> do appreciate it.
>
> I tried your suggestion but I am still experiencing the same problems.
You mean that the EofException did not go away ?
> Incidentally my server and sometimes reports :
>
> 2009-07-23 16:37:08.094::INFO: seeing JVM BUG(s) - cancelling
> interestOps==0
>
> and very occasionally:
>
> 2009-07-23 16:37:08.988::INFO: seeing JVM BUG(s) - recreating selector
>
> after I have subscribed the clients.
These are normal messages that report that Jetty worked around JDK bugs.
> (I am trying to run with 400 clients just now but would be good to get to
> 1000 - also I'm using just one service channel for the responses and another
> channel to push the data to all clients)
>
> My client constructor looks like this:
>
> public TestBayeuxLoadGenerator() throws Exception
> {
> http=new HttpClient();
> http.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
> http.setMaxConnectionsPerAddress(40000);
> http.setIdleTimeout(5000);
> QueuedThreadPool pool = new QueuedThreadPool();
> pool.setMaxThreads(2000);
> pool.setDaemon(true);
> http.setThreadPool(pool);
> http.start();
> }
Looks good.
And what are the results ?
> Any other advice appreciated. This is a very simple comet application. I
> feel it should be possible to set up a working loadgenerator for it.
What is your setup ? You have some other box between client and server
(like Apache or firewalls, load balancers, etc.) ?
This kind of load testing is also present in the Cometd test suite and
we did it several times, and as I said only encountered problems with
the timeouts being similar.
You may have hit something new, but at this point please specify OS
(Linux ?), Jetty version (6.1.19 ?) and boxes setup (client, server,
firewalls, proxies, etc).
If you can attach a simple Maven project that reproduces the problem,
I may find some time to check, but no promises :)
Cheers,