Как мобильному приложению сообщать о готовности отчета при асинхронной генерации
Что проверяют
Понимаете ли вы асинхронный API целиком: как клиент узнаёт о задаче, где хранится её статус и что будет, если приложение свернули. Следом обычно спрашивают, что делать, если push не дошёл.
Как отвечать
Сначала контракт. POST /reports не ждёт генерации, а сразу отвечает 202 Accepted с идентификатором задачи и заголовком Location на GET /reports/{id}. Статус задачи (PENDING, RUNNING, DONE, FAILED) лежит в базе, это единственный источник правды.
Генерацию делает воркер: задача в очереди (Kafka, RabbitMQ) или в таблице заданий. Простой @Async в Spring для этого слаб: при рестарте инстанса задачи из памяти пропадут.
Дальше способы сообщить о готовности, и хороший ответ их комбинирует:
- push-уведомление: FCM для Android, APNs для iOS. Работает, когда приложение в фоне. В payload кладут только id отчёта, сам файл приложение потом забирает по API;
- WebSocket или SSE: мгновенно, пока экран открыт. В Spring это
SseEmitterили WebSocket со STOMP. В фоне мобильная ОС такое соединение быстро рвёт; - опрос статуса: запасной и самый надёжный путь. Интервал растёт (2, 5, 10 секунд), сервер может подсказать его заголовком
Retry-After.
Готовый файл кладут в объектное хранилище и отдают временную подписанную ссылку, чтобы тяжёлая выдача не шла через Java-сервис. Повторное нажатие «Сформировать» не должно порождать второй отчёт: здесь помогает ключ идемпотентности.
Где спотыкаются
Держат HTTP-запрос открытым, пока отчёт строится. Мобильная сеть, балансировщик или таймаут клиента обрывают его через десятки секунд, а работа на сервере продолжается впустую.
Считают push гарантированной доставкой. Уведомление может задержаться, схлопнуться с другими или не прийти, если пользователь их отключил. Поэтому при открытии экрана приложение всё равно спрашивает статус.
Выбирают только WebSocket. Как только приложение уходит в фон, соединение закрывается, и сигнал о готовности теряется.