@Arpanet256
Постигаю Java

Ошибка Inappropriate blocking method call при http запросе в Kotlin. В чем проблема?

Изучаю корутины и возник вопрос. Набросал простую функцию для получения данных из сети. Но в Intelij IDEA выделяет желтым URL(url).openConnection() и выводит предупреждение "Inappropriate blocking method call"

Пробовал вызывать так
runBlocking { 
    getHttp("http://link.ru")
}

Ставил модификатор suspend для fun main - один фиг выделяет. В чем ошибка?
suspend fun getHttp(url: String): String {
    var txt = ""
    withContext(Dispatchers.IO) {
        with(URL(url).openConnection() as HttpURLConnection) {
            try {
                txt = inputStream.bufferedReader().readText()
            } catch (e:Exception) {
                e.printStackTrace()
            }
        }
    }
    return txt
}

Насколько я понял, HttpUrlconnection вызывается блокирующе и может бросить исключение в виде IOException. По ходу вся суть использования корутин тогда сходит на нет, но это не точно...
  • Вопрос задан
  • 116 просмотров
Решения вопроса 3
zagayevskiy
@zagayevskiy Куратор тега Java
Android developer at Yandex
Когда ты так делаешь, весь смысл корутин теряется. Фишка в том, что когда корутина саспендится, поток не блокируется. А ты внутри корутины используешь блокирующий метод. Возьми другой http клиент, ktor, он, вроде как, умеет нормально это делать.
Ответ написан
@Asapin
В свободное время ковыряюсь с Rust и Wasm
Если по каким-то причинам нет возможности заменить блокирующий код неблокирующим аналогом (например, драйвер БД, для которого асинхронной версии просто не успели ещё написать), то можно использовать такое решение:
1. Создаёте отдельный тредпул
2. Все вызовы блокирующего кода оборачиваете в блок
suspendCoroutine { continuation ->
    //
}

3. Внутри этого блока создаёте Runnable, который и будет выполнять ваш блокирующий код. После выполнения блокирующего кода вызываете либо continuation.resume(result), либо continuation.resumeWithException(e)
4. Скармливаете полученный Runnable тредпулу.

Это приведёт к тому, что оригинальная корутина засаспендится, блокирующий код будет выполняться в отдельных потоках, не мешая* остальным корутинам, и когда блокирующий код завершится, корутина будет разбужена с готовым результатом.

* - В котлине по умолчанию используется диспатчер Dispatcher.Default, количество потоков в котором равно количеству ядер CPU (но не меньше двух), поэтому добавление отдельного тредпула под блокирующие задачи приведёт к увеличению количества тредов, что в экстремальных случаях может привести к общей деградации производительности. Но в если у вас не высоконагруженное приложение, то этим можно пренебречь. Правда не уверен как с этим обстоят дела на мобильных платформах, возможно там количество тредов более критично.

Из очевидных минусов:
  • количество потоков больше, чем ядер CPU
  • количество одновременно выполняемых блокирующих методов = количеству потоков в этом тредпуле
  • следите за тем, что бы этот тредпул был ограничен сверху
.

Насколько я помню, еще ~1.5 года назад примерно такая схема применялась в котлиновских обёртках над спринговыми драйверами то ли к монго, то ли к редису (там было 3 версии драйвера - блокирующая, реактивная, и блокирующая, но обёрнутая в отдельный тредпул, как я описал). Как сейчас - не знаю.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы