簡體   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