繁体   English   中英

通过Apache Http Client(和java.util.concurrent)的并发请求似乎正确完成但崩溃了我的操作系统

[英]Concurrent requests via Apache Http Client (and java.util.concurrent) seem to finish correctly but crash my OS

我目前正在学习使用java.util.concurrent包提供的Java的并发功能。 作为练习,我尝试编写一个可用于性能测试HTTP API的小程序。 但不知怎的,我的程序并没有经常正确终止。 它甚至崩溃了我的操作系统。

以下是我的程序伪代码

  • 实例化请求对象,查询HTTP API(在示例中,我只查询一个随机站点)。
  • 实例化多个Callables,其中每个Callables代表一个Http Call。
  • 迭代Callables并通过ScheduledExecutorService安排它们(可以在代码的开头配置每秒应执行的请求数)。
  • 在安排所有Callables之后,我开始迭代Futures。 如果期货已完成,则检索响应。 每隔一秒做一次。 如果没有完成新的Future,请退出循环。

我在细节方面遇到了什么问题?

  • 很多时候,程序没有正确完成。 我在控制台中看到所有日志打印,就像程序正确完成一样。 但实际上我看到eclipse中的停止按钮仍然保持活动状态 停 如果我点击它,它表示该程序无法正确终止。 无论我多久等待它都没有完成(注意:我在eclipse中启动程序)。
  • 如果我增加请求数,我可以轻松地引发错误。 如果我要到2000年,这肯定会发生。 如果它发生我的操作系统甚至崩溃,我仍然可以使用eclipse,但其他应用程序不再工作。
  • My Environment是Mac OS X 10.7上的Eclipse 3.7,带有Java 1.6和Apache httpclient 4.2.2

你在我的代码中发现了任何主要的错误吗? 之前我从未在java程序中遇到过这样的问题,因为我的操作系统崩溃并且根本没有看到任何异常。

编码:

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;
        }
    }

}

永远不要在循环中这样做:

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

它可能会导致关机问题。

此外,您的大部分代码都可以通过对ExecutorService.invokeAll()的单次调用来替换,因此请尝试并查看您是否有更多运气。

最后,当您不知道Java应用程序在做什么时,请运行jconsole ,附加到应用程序,然后查看线程堆栈以查看当前正在进行的代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM