OkHttp: пул соединений и дескрипторы файлов
Мы используем Retrofit/OkHttp3 для всего сетевого трафика из нашего приложения для Android. Пока что все идет довольно гладко.
Тем не менее, у нас теперь время от времени заканчивались дескрипторы файлов в нашем приложении / процессе.
- Android позволяет использовать до 1024 файловых дескрипторов на процесс
- OkHttp создаст новый поток для каждого асинхронного вызова
- Каждый поток, созданный таким образом, будет (по нашему наблюдению) отвечать за 3 новых дескриптора файла (2 канала и один сокет).
Мы смогли отладить это точно, где каждый отправленный асинхронный вызов, используя
.enqueue()
приведет к увеличению количества открытых файловых дескрипторов на 3.
Проблема в том, что ConnectionPool
в OkHttp, кажется, поддерживает потоки соединения гораздо дольше, чем они на самом деле нужны. ( Этот пост говорит о пяти минутах, хотя я нигде не видел, чтобы это было указано.)
- Это означает, что если вы быстро отправляете запрос, пул подключений будет увеличиваться в размере, равно как и число обработчиков файлов - пока вы не достигнете 1024, где происходит сбой приложения.
Я видел, что можно ограничить количество параллельных вызовов с Dispatcher.setMaxRequests()
(хотя кажется неясным, работает ли это на самом деле, см. здесь) - но это все еще не решает проблему с накоплением открытых потоков и файловых дескрипторов.
Как мы можем помешать OkHttp создавать слишком много файловых дескрипторов?
1 ответ
Я отвечаю на свой вопрос здесь, чтобы задокументировать этот вопрос, который у нас был. Нам потребовалось некоторое время, чтобы понять это, и я думаю, что другие тоже могут столкнуться с этим и могут быть рады этому ответу.
Наша проблема заключалась в том, что мы создали один OkHttpClient
для каждого запроса, поскольку мы использовали его API-интерфейс конструктора / перехватчика для настройки некоторых параметров для каждого запроса, таких как заголовки HTTP или тайм-ауты.
По умолчанию каждый OkHttpClient
поставляется с собственным пулом соединений, что, конечно, увеличивает количество соединений / потоков / файловых дескрипторов и предотвращает правильное повторное использование в пуле.
Наше решение
Мы решили проблему, вручную создав глобальную ConnectionPool
в одиночку, а затем передать это OkHttpClient.Builder
объект, который строит фактический OkHttpClient
,
- Это по-прежнему позволяет для конфигурации по запросу с использованием
OkHttpClient.Builder
- Уверен все
OkHttpClient
экземпляры все еще используют общий пул соединений.
Мы смогли правильно определить глобальный пул соединений.