0

i am currently learning to use the the concurrent features of Java provided by the package java.util.concurrent. As an exercise i tried to write a little program that could be used to performance test a HTTP API. But somehow my program is not terminating correctly very often. It even crashes my OS.

Following is the pseudo code of my program:

  • Instantiate Request Objects, that query an HTTP API (In the example i just query one random site).
  • Instantiate multiple Callables, where each one represents a represents an Http Call.
  • Iterate over the Callables and schedule them via a ScheduledExecutorService (how many requests should be performed per second can be configured at the begin of the code).
  • After scheduling all Callables, i am beginning to iterate over the Futures. If a futures is done, retrieve the response. Do this every second. If no new Future was finished, quit the loop.

What problems am i experiencing in detail?

  • Lots of times, the program is not finishing correctly. I see all log prints in the console, as if the program is finishing correctly. But actually i am seeing that stop button in eclipse still remains active stop. If i click it, it says that the program could not be terminated correctly. It does not finish no matter how i long i wait (NOTE: I am starting the program inside eclipse).
  • I can provoke the error easily if i am increasing the number of Requests. If am turning up to 2000, this will happen for sure. If it happens my OS even crashes, i can still use eclipse, but other apps do not work anymore.
  • My Environment is Eclipse 3.7 on Mac OS X 10.7 with Java 1.6 and Apache httpclient 4.2.2

Do you spot any major erros in my code? Before i have never had such issues in a java program with crashing my OS and seeing no exceptions at all.

The code:

public class ConcurrentHttpRequestsTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(25);

        Integer standardTimeout = 5000;
        Float numberOfRequestsPerSecond = 50.0f;
        Integer numberOfRequests = 500;
        Integer durationBetweenRequests = Math.round(1000 / numberOfRequestsPerSecond);

        // build Http Request
        HttpGet request = null;
        request = new HttpGet("http://www.spiegel.de");
        // request.addHeader("Accept", "application/json");
        HttpParams params = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(params, standardTimeout);
        HttpConnectionParams.setSoTimeout(params, standardTimeout);
        request.setParams(params);

        // setup concurrency logic
        Collection<Callable<Long>> callables = new LinkedList<Callable<Long>>();
        for (int i = 1; i <= numberOfRequests; i++) {
            HttpClient client = new DefaultHttpClient();
            callables.add(new UriCallable(request, client));
        }

        // start performing requests
        int i = 1;
        Collection<Future<Long>> futures = new LinkedList<Future<Long>>();
        for (Callable<Long> callable : callables) {
            ScheduledFuture<Long> future = scheduledExecutorService.schedule(callable, i * durationBetweenRequests, TimeUnit.MILLISECONDS);
            futures.add(future);
            i++;
        }

        // process futures (check wether they are ready yet)
        Integer maximumNoChangeCount = 5;
        boolean futuresAreReady = false;
        int noChangeCount = 0;
        int errorCount = 0;
        List<Long> responses = new LinkedList<Long>();
        while (!futuresAreReady) {
            boolean allFuturesAreDone = true;
            boolean atLeast1FutureIsDone = false;
            Iterator<Future<Long>> iterator = futures.iterator();

            while (iterator.hasNext()) {
                Future<Long> future = iterator.next();
                allFuturesAreDone = allFuturesAreDone && (future.isDone());
                if (future.isDone()) {
                    try {
                        atLeast1FutureIsDone = true;
                        responses.add(future.get());
                        iterator.remove();
                    } catch (Exception e) {
                        // remove failed futures (e.g. timeout)
                        // System.out.println("Reached catch of future.get()" +
                        // e.getClass() + " " + e.getCause().getClass() + " " +
                        // e.getMessage());

                        iterator.remove();
                        errorCount++;
                    }
                }
                if (future.isCancelled()) {
                    // this code is never reached. Just here to make sure that
                    // this is not the cause of problems.
                    System.out.println("Found a cancelled future. Will remove it.");
                    iterator.remove();
                }
            }
            if (!atLeast1FutureIsDone) {
                System.out.println("At least 1 future was not done. Current noChangeCount:" + noChangeCount);
                noChangeCount++;
            } else {
                // reset noChangeCount
                noChangeCount = 0;
            }
            futuresAreReady = allFuturesAreDone;

            // log the current state of responses, errors and remaining futures
            System.out.println("Size of responses :" + responses.size() + "; Size of futures:" + futures.size() + " Errors:" + errorCount);
            if (noChangeCount >= maximumNoChangeCount) {
                System.out.println("Breaking while loop becauce no new future finished in the last " + maximumNoChangeCount + " iterations");
                break;
            }
            // check every second
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        for (Long responsetime : responses) {
            // analyze responsetimes or whatever
        }
        // clean up
        // .shutdown() made even more problems than shutdownNow()
        scheduledExecutorService.shutdownNow();
        System.out.println("Executors have been shutdown - Main Method finished. Will exit System.");
        System.out.flush();
        System.exit(0);
    }

    private static class UriCallable implements Callable<Long> {
        private HttpUriRequest request;
        private HttpClient client;

        public UriCallable(HttpUriRequest request, HttpClient client) {
            super();
            this.request = request;
            this.client = client;
        }

        public Long call() throws Exception {
            Long start = System.currentTimeMillis();
            HttpResponse httpResponse = client.execute(request);
            Long end = System.currentTimeMillis();
            return end - start;
        }
    }

}
mavilein
  • 11,648
  • 4
  • 43
  • 48

1 Answers1

2

Never do this in a loop:

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

It might cause problems on shutdown.

Also, most of your code could be replaced by a single call to ExecutorService.invokeAll(), so try that and see if you have more luck.

Lastly, when you don't know what your Java application is doing, run jconsole, attach to the application, and look at the thread stacks to see what code is currently in progress.

artbristol
  • 32,010
  • 5
  • 70
  • 103