BindException/слишком много файлов открыто при использовании HttpClient под нагрузкой

У меня есть 1000 выделенных потоков Java, где каждый поток опрашивает соответствующий URL-адрес каждую секунду.

public class Poller { 
    public static Node poll(Node node) { 
        GetMethod method =  null; 
        try { 
            HttpClient client = new HttpClient(new SimpleHttpConnectionManager(true)); 
            ......
        } catch (IOException ex) { 
            ex.printStackTrace(); 
        } finally { 
            method.releaseConnection(); 
        } 
    } 
} 

Потоки запускаются каждую секунду:

for (int i=0; i <1000; i++) { 
    MyThread thread = threads.get(i) // threads  is a static field 
    if(thread.isAlive()) { 
        // If the previous thread is still running, let it run. 
    } else { 
        thread.start(); 
    } 
}

Проблема в том, что если я запускаю задание каждую секунду, я получаю случайные исключения, подобные этим:

java.net.BindException: Address already in use 
 INFO httpclient.HttpMethodDirector: I/O exception (java.net.BindException) caught when processing request: Address already in use 
 INFO httpclient.HttpMethodDirector: Retrying request 

Но если я запускаю задание каждые 2 секунды или чаще, все работает нормально.

Я даже пытался закрыть экземпляр SimpleHttpConnectionManager() с помощью функции shutdown(), но безрезультатно.

Если я делаю netstat, я вижу тысячи TCP-соединений в состоянии TIME_WAIT, что означает, что они были закрыты и очищаются.

Итак, чтобы ограничить количество подключений, я попытался использовать один экземпляр HttpClient и использовать его следующим образом:

  public class MyHttpClientFactory { 
        private static MyHttpClientFactory instance = new HttpClientFactory(); 
        private MultiThreadedHttpConnectionManager connectionManager; 
        private HttpClient client; 

        private HttpClientFactory() { 
                init(); 
        } 

        public static HttpClientFactory getInstance() { 
                return instance; 
        } 

        public void init() { 
                connectionManager = new MultiThreadedHttpConnectionManager(); 
                HttpConnectionManagerParams managerParams = new HttpConnectionManagerParams(); 
                managerParams.setMaxTotalConnections(1000); 
                connectionManager.setParams(managerParams); 
                client = new HttpClient(connectionManager); 
        } 

        public HttpClient getHttpClient() { 
                if (client != null) { 
                        return client; 
                } else { 
                    init(); 
                    return client; 
                } 
        } 
}

Однако после работы ровно 2 часа он начинает выдавать «слишком много открытых файлов» и в конечном итоге вообще ничего не может сделать.

ERROR java.net.SocketException: Too many open files
INFO httpclient.HttpMethodDirector: I/O exception (java.net.SocketException) caught when processing request: Too many open files
INFO httpclient.HttpMethodDirector: Retrying request

Я должен был бы увеличить количество разрешенных подключений и заставить его работать, но я бы просто продлил зло. Есть идеи, как лучше всего использовать HttpClient в ситуации, подобной описанной выше?

Кстати, я все еще на HttpClient3.1.


person Langali    schedule 26.05.2010    source источник


Ответы (3)


Это случилось с нами несколько месяцев назад. Во-первых, проверьте дважды, чтобы убедиться, что вы действительно вызываете releaseConnection() каждый раз. Но даже в этом случае ОС фактически не восстанавливает все соединения TCP сразу. Решение заключается в использовании MultiThreadedHttpConnectionManager. Это объединяет и повторно использует соединения.

См. http://hc.apache.org/httpclient-3.x/performance.html для получения дополнительных советов по повышению производительности.

Обновление: упс, я не читал нижний пример кода. Если вы выполняете releaseConnection() и используете MultiThreadedHttpConnectionManager, подумайте, достаточно ли установлено ограничение ОС на количество открытых файлов для каждого процесса. У нас тоже была эта проблема, и нам нужно было немного расширить лимит.

person Jim Ferrans    schedule 26.05.2010
comment
@Langali: О да, это научит меня читать пост полностью! Еще одна вещь, которую следует учитывать, - возможно, установлено слишком низкое ограничение вашей ОС на количество открытых файлов на процесс. Мы расширили наши, когда обнаружили, что Glassfish использует почти все свои ресурсы для загрузки классов и т. д. Это решило проблему для нас. - person Jim Ferrans; 26.05.2010

В первой ошибке нет ничего плохого. Вы просто исчерпали доступные эмпирические порты. Каждое соединение TCP может оставаться в состоянии TIME_WAIT в течение 2 минут. Вы генерируете 2000/секунд. Рано или поздно сокет не сможет найти неиспользуемый локальный порт, и вы получите эту ошибку. TIME_WAIT предназначен именно для этой цели. Без него ваша система может перехватить предыдущее соединение.

Вторая ошибка означает, что у вас открыто слишком много сокетов. В некоторых системах существует ограничение в 1K открытых файлов. Возможно, вы просто достигли этого предела из-за затянувшихся сокетов и других открытых файлов. В Linux вы можете изменить это ограничение, используя

  ulimit -n 2048

Но это ограничено общесистемным максимальным значением.

person ZZ Coder    schedule 26.05.2010

От имени sudo или root отредактируйте файл /etc/security/limits.conf. В конце файла чуть выше «# End of File» введите следующие значения: * soft nofile 65535 * hard nofile 65535 Это установит неограниченное количество открытых файлов.

person jacktrade    schedule 20.08.2012