Архитектурные вопросы по созданию API для асинхронного сервиса

Так получилось, что мне одному надо разработать "асинхронное API". Поясню, что я вкладываю в термин "асинхронное API", общая архитектура такова:
Есть листенер, который принимает http запросы к API, на основе запроса составляется сообщение которое отправляется на AQMP брокер. Есть воркеры, которые забирают сообщение из очереди (AMQP) и выполняют какие-то действия, которые указанны в полученном сообщении. Выполнение запросов могут занимать продолжительное время. Результатом работы могут быть XML или JSON данные (в некоторых, очень редких, случаях это может быть PDF\RTF). Пользователь делает запрос, ему отдается Task ID. Далее пользователь, с какой-то периодичностью, проверяет статус этой задачи (делает Get запрос, например на адрес host/api/task/123-1213-12121-121212).
Собственно сам вопрос, каким образом можно хранить и возвращать результат работы?
Я вижу несколько решений:
1) Результат работы помещается в таблицу БД. Таблица, например, из 3 полей - taskID, result, time (в time хранить время записи результат в таблицу, что бы удалять, например, спустя 48 часов. )
2) Результат работы помещать в другу. очередь на AMQP брокере. Но как потом отдавать? Как идентифицировать конкретное сообщение в очереди без перебора всех?
3) Еще какой-то вариант до которого я еще не додумался.

Буду очень рад за советы (по абсолютно любым пунктам). Если интересуют конкретные технологии то вот:
1) Листенер - python + bottle + pika
2) Воркеры - python + pika + много чего =)
3) В качестве базы Postgresql

Очень надеюсь на вашу помощь, т.к. посоветоваться не с кем. Этот проект затеян для самообразования.
  • Вопрос задан
  • 3642 просмотра
Решения вопроса 1
@DeBass Автор вопроса
Спасибо за ответы!
В данный момент реализовал так:
На листенер приходит запрос, после всех проверок (токены, схема json, допустимые параметры) в таблицу tasks(task_id varchar(50), status varchar(10), result text, timest timestamt ) заноситься id задачи и статус "new", после передачи в очередь статус меняется на "queue". как только воркер достает сообщение из очереди он смотрит taskId и меняет статус на "inWork", после выполнения статус меняется на "complete" и записывается результат. При каждом обновлении статуса происходит обновление поля timest. Пользователь проверяет статус задачи get запросом и если статус !=complete то ему возвращается текущий статус и таймштамп. если статус = complete, то пользователю возвращается поле result и статус меняется на "was_returned" (далее скрипт в фоне делитит записи со статусом "was_returned", которые старше 48 часов и записи "completed", которые старше недели). Как вам такая схема работы?
Варианты с пушем очень интересные обязательно попробую их попозже.
Ответ написан
Пригласить эксперта
Ответы на вопрос 4
zarincheg
@zarincheg
Если пользователь работает через браузер, то можно сделать пуш-уведомления используя web-сокеты или long polling. Ну и собственно либо прямо так данные передавать, либо инициировать запрос данных этим самым уведомлением. А результаты хранить в базе нормально.
Ответ написан
madmages
@madmages
Человек прямоходящий
если нет задачи тянуть старые браузеры то вебсокет будет гуд, если старые браузеры то все же long poling (если внапряг держать дофига соединений то рядом поставить проксирующий nodejs, он мало ресурсов требует для для поддержки соединений), ну а если это не подходит то ваш первый вариант вам в руки.
Ответ написан
Комментировать
maxaon
@maxaon
Если брокер сообщений поддерживает task status используйте его. Также, желательно, пользователю сообщать состояние задачи.
Передавать статусы согобщений, как сказали ранее, через websocket или longpolling.
Результат заносить в БД, отдавать другим запросом.
Если брокер не поддерживает сохранение состояния, то дублируйте результат(pending,progress,sucess,fail) в БД из скрипта обработчика.
Ответ написан
Комментировать
@skomoroh
api на django+tastypie хорошо получаются

в api 3 метода:
1. поставить задачу - в ответ id задачи
2. узнать статус задачи по id - в работе, ошибка, готово
3. получить результат по id

листенер:
под каждый таск берет из редиски id ( инкремент )
сразу кидает в очередь id+таск без разбора
в редис пишет этому id статус "в работе"
на все запросы статуса и перед забором результата берет статус из редиски
если статус "готово", берет результат из базы ( или из файла )

воркер первой ступени:
берет таски из очереди,
проверяет данные, если ошибочные( или баланс юзера и т.д. ) - пишет в редис статус "ошибка" с пояснением,
разбивает на микро-таски, микро-таски кидает в новую очередь,
в постгрю или редиску пишет инфу по связям тасков и микротасков ( task_id, sub_task_id, status, result )

воркеры 2-й ступени:
берут микро-таск из очереди
выполняют
кидают id+результат в очередь результатов

воркер 3-й ступени:
берет стопками результаты микро-тасков
групирует их по таскам в постгре или редиске
если все микро-таски готовы, пишет результат таска в постргрю или файл
меняет статус таска на готово или ошибка( если результаты микро-тасков ошибка )

время хранения результата задавать через ттл редиски
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Похожие вопросы