8

For testing/benchmarking purposes, I want to write a Java program that does the following tasks in a loop:

  1. load data via HTTP GET from a server
  2. ( generate an answer based on the received data - not important at this point )
  3. send the answer via HTTP POST to the same server

This cycle runs on multiple threads at the same time.

After having started, the program runs fine for a short period of time and is able to perform ~300 cycles per thread per second (the webserver runs on the same machine). But after 5-7 seconds, I'm getting BindException: Address already in use.

Restarting the program after a 20-30 second cool-down time results in the same behavior; when I restart it immediately without waiting, it crashes immediately... so I suppose it could be a problem with bound resources.

It's a quick and dirty approach using HttpURLConnection. The relevant parts:

Getting data from the webserver

public String fetchData() throws IOException {

        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setUseCaches(false);
        conn.setRequestMethod("GET");

        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }

        in.close();
        conn.disconnect();

        return response.toString();
    }

Sending the answer

public void sendData(byte[] data) throws IOException {

        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setUseCaches(false);
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);

        OutputStream os = conn.getOutputStream();
        os.write(data);    
        os.close();
        conn.disconnect();
    }

Calling both methods inside the thread

@Override
public void run() {
    while(true) {
        try {
            String data = fetchData();
            String answer = // ... generating answer
            sendData(answer.getBytes("UTF-8"));
        } catch (IOException e) {
            // ...
        }
    }
}

There is not a single URL object that is shared between threads - every thread has its own URL instance (however, each instance points to the same address).

edit:

Here's the stack trace of the very first exception that occurs:

java.net.BindException: Address already in use: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at sun.net.NetworkClient.doConnect(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.http.HttpClient.openServer(Unknown Source)
    at sun.net.www.http.HttpClient.<init>(Unknown Source)
    at sun.net.www.http.HttpClient.New(Unknown Source)
    at sun.net.www.http.HttpClient.New(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at PayloadProcessor.fetchData(PayloadProcessor.java:66)
    at PayloadProcessor.run(PayloadProcessor.java:32)
    at java.lang.Thread.run(Unknown Source)

It occurs in the fetchdata method at the following line:

in = new BufferedReader(new InputStreamReader(conn.getInputStream()));

I removed the calls to disconnect() (as pointed out by Aaron) - unfortunately, the same problem still exists.


Let's assume everything is closed correctly (not sure if this is the case here, just let assume it) - could it be the case that the program is just too fast? I found another post on stackoverflow, the "solution" was to add a simple Thread.sleep - but since it is a benchmark and should run as fast as possible, I don't like that. How do load testing tools handle this problem?


I reduced the amount of threads to 1, even then the problem occurs.

Community
  • 1
  • 1
ceran
  • 1,392
  • 1
  • 17
  • 43
  • 1
    Posting here the complete stack trace of the error might be useful. Anyway, the 1st thing I would recommend to you is to enclose the _disconnect_ invokations into a _finally_ clause: Maybe so many exceptions are rising that the connections are not being propperly closed, causing a leakage. – Little Santi Sep 21 '14 at 23:15
  • Thanks, I added the stack trace. You're right, there were indeed many exceptions - but for debugging purposes, I now terminate the program after the first exception raises (not shown in the code above) and still have the same problem. The very first exception already is a BindException. – ceran Sep 22 '14 at 10:54
  • Please add results of command `netstat -anpt | grep ` or its analog in your OS. – user1516873 Sep 26 '14 at 07:14
  • You have a many connection with one server. Web-server(or servlet-container) has a limit for connection. What server you use? – Vitalii Pro Sep 26 '14 at 09:35

5 Answers5

2

It sounds like you may be running out of local ports. Is it possible that there are too many connections being spawned in parallel and are not releasing their resources fast enough for other threads to reuse?

I would get rid of the call to disconnect() as that will pretty much disable connection pooling for you. From the docs:

Indicates that other requests to the server are unlikely in the near future. Calling disconnect() should not imply that this HttpURLConnection instance can be reused for other requests.

Aaron
  • 10,386
  • 13
  • 37
  • 53
  • Thanks. I removed both of the calls to `disconnect()` but unfortunately it didn't change anything. I share your opinion regarding the lack of ports, but I don't know why it happens. Is the program just too fast? See my edit – ceran Sep 22 '14 at 10:56
  • Hmm...something is definitely wrong. Could you show us what's in your catch block? – Aaron Sep 24 '14 at 12:58
1

I think you are running out of local port to check this: Run you program and run netstat command. Try using connection pooling for http connection HTTP connection pooling using HttpClient

Community
  • 1
  • 1
aianand
  • 11
  • 3
  • I would also advise on using HttpClient instead of direct HttpURLConnection. There are all sorts of horrible things that can go wrong using HttpURLConnection directly that HttpClient will just take care of for you. – hooknc Sep 29 '14 at 21:39
0

Looks like you are running out of local ports. To prevent this, you have to make sure that your connections are reused. Java will try to do this for you, but you need to read the entire response for every request.

While fetchData function does read responses, sendData doesn't. While you don't need response body here, you should read it anyway (up to the end) and then close the corresponding InputStream. Then this connection might be reused.

Also don't disconnect your connections.

This question might be helpful.

abacabadabacaba
  • 2,662
  • 1
  • 13
  • 18
0

To fetch and send data, you create one HttpURLConnection per every fetchData and sendData call. That means, you are making too many connections to the server. As already pointed out, this will make all your available local port numbers run out.

Just create one HttpURLConnection per thread. I strongly suggest you to refactor the given code as follows:

@Override
public void run() {
    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
    while(true) {
        try {
            String data = fetchData(conn);
            String answer = // ... generating answer
            sendData(conn, answer.getBytes("UTF-8"));
        } catch (IOException e) {
            // ...
        }
    }
}

That is, you create the HttpURLConnection object once outside of while loop, and pass it to the fetchData and sendData as an argument.

Byungjoon Lee
  • 913
  • 6
  • 18
0

Simple solution is just introduce a delay before answer.

String data = fetchData(conn);
String answer = // ... generating answer
Thread.sleep(1000);
sendData(conn, answer.getBytes("UTF-8"));
thangamanikasi
  • 846
  • 6
  • 19