Ошибка №1: Блокирование корутин-диспетчеров. Это, пожалуй, самая распространенная и опасная проблема. Ktor построен на корутинах, и по умолчанию использует диспетчер `Dispatchers.IO` для ввода-вывода и `Dispatchers.Default` для CPU-операций. Ошибка — выполнять блокирующие операции (например, вызов блокирующего JDBC-драйвера, синхронный сетевой запрос, или просто `Thread.sleep()`) внутри корутины без специального диспетчера.
Симптомы: Приложение «зависает» под нагрузкой, падает производительность, неиспользуемые ядра CPU, растущее количество потоков.
Решение: Всегда оборачивайте блокирующий вызов в `withContext(Dispatchers.IO) { ... }`. Если вы используете блокирующую библиотеку для БД (например, стандартный JDBC), рассмотрите переход на асинхронные драйверы (например, R2DBC для PostgreSQL) или используйте отдельный fixed thread pool через `newFixedThreadPoolContext`. Шаг за шагом: 1) Выявите блокирующие вызовы с помощью профайлера или анализа стека. 2) Изолируйте их в `withContext`. 3) Никогда не используйте `Dispatchers.Default` или `Dispatchers.Main` для операций ввода-вывода.
Ошибка №2: Неправильная обработка исключений и статусов ответов. Разработчики часто полагаются на стандартный обработчик ошибок Ktor или просто позволяют исключениям проваливаться, что приводит к ответам `500 Internal Server Error` без полезной информации.
Симптомы: Клиенты получают непонятные 500 ошибки, в логах есть исключения, но нет структурированной информации для отладки.
Решение: Реализуйте централизованный обработчик исключений с помощью `StatusPages` плагина. Пошаговая инструкция:
- Установите плагин: `install(StatusPages)`
- В блоке `exception { call, cause -> }` перехватывайте все исключения.
- Логируйте исключение с контекстом (например, `call.request.uri`).
- В зависимости от типа исключения (`IllegalArgumentException`, `NotFoundException`), отправляйте соответствующий HTTP-статус (400, 404) с понятным телом в JSON.
- Для непредвиденных исключений все равно возвращайте 500, но с уникальным ID ошибки для отслеживания в логах. Никогда не пробрасывайте стектрейс на клиент.
Симптомы: «Подвисающие» запросы, накопление незавершенных соединений, каскадные сбои при отказе внешнего сервиса.
Решение: Всегда настраивайте таймауты на уровне HttpClient (для клиента) и на уровне engine (для сервера). Для клиента: при создании `HttpClient` используйте `HttpClientConfig` для настройки `HttpTimeout`. Установите `socketTimeoutMillis`, `connectTimeoutMillis`, `requestTimeoutMillis`. Для сложных сценариев используйте `Retry` плагин из Ktor Client с политикой экспоненциальной отсрочки (exponential backoff). На сервере: настройте `connectionTimeout` и `requestTimeout` в конфигурации engine (например, Netty или CIO).
Ошибка №4: Неэффективная маршрутизация и порядок установки плагинов. Порядок объявления маршрутов (`routing`) и установки плагинов (`install`) имеет значение, так как пайплайн обработки запроса выполняется последовательно.
Симптомы: Некоторые маршруты не обрабатываются, плагины (например, аутентификация) не применяются к нужным эндпоинтам, падение производительности.
Решение: Следуйте принципу «от общего к частному» и «плагины перед маршрутизацией». Пошагово:
- Устанавливайте плагины (CORS, Compression, Authentication, StatusPages) ДО объявления блока `routing`. Это гарантирует, что они будут применены ко всем последующим маршрутам.
- Внутри `routing` структурируйте пути. Общие фильтры (например, `authenticate`) применяйте к целым группам (`route("/api") { authenticate("auth-jwt") { ... } }`).
- Избегайте вложенных `routing` блоков, если в этом нет необходимости — это усложняет отладку.
Симптомы: Операции продолжают выполняться после завершения запроса, утечки памяти, нелогичные отмены задач.
Решение: Всегда используйте `call` или `ApplicationCall` как источник `CoroutineScope`. Для запуска фоновой задачи, которая должна пережить запрос (например, отправка email), создавайте корутину в scope приложения (`application.environment.monitor`), но с осторожностью и возможностью отмены. Для задач в рамках запроса используйте `launch` без явного указания scope — он будет автоматически привязан к scope запроса и отменен с его завершением.
Заключительная рекомендация: Ktor дает свободу, но требует дисциплины. Внедрите статический анализ (Detekt, ktlint), пишите интеграционные тесты, проверяющие не только happy path, но и таймауты, и обработку ошибок. Мониторьте метрики: количество активных корутин, время обработки запросов, частоту ошибок. Понимание этих типичных ошибок и следование пошаговым инструкциям позволит вам строить на Ktor не только быстрые прототипы, но и надежные, отказоустойчивые production-приложения.
Комментарии (7)