Вопрос по поводу API



Асторологи объявили день обсуждения API - у меня тоже вопрос про API. В одной из наших групп разработки сейчас делают платформу для новых сервисов, и поступило предложение задизайнить весь API под CQRS и асинхронную модель.
Предлагается на любой "чих" вместо синхронной операции и ответа общение разбить на 2 этапа: (1) постановка задания в очередь и ответ с идентификатором задания, и (2) через отдельный API идет получение результатов выполнения этого задания. После того, как мы поштормили, я склонен согласиться, что это достаточно мощный инструмент и у него есть ряд плюсов (асинхронность из коробки, значит более отзывчивый интерфейс + более удобное разделение компонент процессинга команд + маштабирование не через аппликейшен-слой а через слой обработчиков событий это может добавить удобства и сделать аппликейшен-слой менее "убиваемым").
Что меня лично смутило: что этот метод предлагается использовать по умолчанию. В моей практике задач, которые бы обязательно требовали асинхронного исполнения был примерно 1%. В минусах - чуть больше телодвижений со стороны девелоперов при разработке, и пока не очень понятно, насколько это всё будет удобно в отладке/тестировании.
Важное пояснение: предполагается сделать синхронную сервис-обертку вокруг этого API такую что если какому-то внешнему клиенту потребуется синхронный API, то он будет достаточно легко предоставлен путем "разрешения" в конфиге.
Вопрос: кто-нибудь проектировал апи по такой модели by default? Можете поделиться lessons learned?
784   75  

Comments

  1. Макс Лапшин
    Макс Лапшин 4 года назад
    любая асинхронщина в разы сложнее, тяжелее и дороже синхронщины.
  2. Алексей Шурыгин
    Алексей Шурыгин 4 года назад
    Это все имеет смысл при больших объемах данных и большом количестве запросов. Начните с выяснения надо ли вам это делать
  3. Дмитрий Юркин
    Дмитрий Юркин 4 года назад
    Под такую систему несколько сложнее делать интеграцию. С точки зрения владельца системы выгоднее, прежде всего при высоких нагрузках позволит гибко масштабировать, но общая сложность системы вырастет.
  4. Андрей Иванников
    Андрей Иванников 4 года назад
    Тут не хватает ключевого - а за что боремся? Будет точно дольше в разработке и в суппорте.
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Андрей Иванников борятся за маштабируемость и UX
    • Андрей Иванников
      Андрей Иванников 4 года назад
      Alexey Rybak не обязательно, но мы делали через ассинхронную шину
    • Андрей Иванников
      Андрей Иванников 4 года назад
      Задачи, что писал ты выше оно решило. Но добавило сильно больше костов на разработку и суппорт, чем мы ожидали. Плюс из 50 соискателей, 1-2 могут хоть как-то внятно рассказать смысл теории, не говорю уже про практику
    • Андрей Иванников
      Андрей Иванников 4 года назад
      Да, в стартапе лично я бы не делал ни CQRS, ни event sourcing.
  5. Стас Щетинников
    Стас Щетинников 4 года назад
    То, что описывается - это не event sourcing (от слова совсем). Ну и большого смысла от перехода на асинхронное взаимодействие вместо синхронного - тоже нет никакого.
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Стас Щетинников я согласен с тем, что event sourcing тут просто мешает (хотя аналогия понятна - последовательность событий над сущностями) - убрал это, оставил просто асинхронность.
  6. Макс Барышников
    Макс Барышников 4 года назад
    У нас есть похожее: https://m.habr.com/ru/company/wargaming/blog/441708/ (мотай до Contract API)
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Макс Барышников вы это используете по умолчанию для любого API и всё-таки по умолчанию всё синхронно?
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Макс Барышников поэтому я и написал асинхронный CQRS.
  7. Петр Чаянов
    Петр Чаянов 4 года назад
    Одно другому не мешает, я у себя делал когда запрос может сразу ответить результатом или ошибкой, а может и вернуть токен для последующего ответа. Через апи можно проверять как там дела по этому токену, через время будет ответ. У нас много оборудования которое может долго отвечать и ответ по такому запросу может например минут через 15 прийти
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Петр Чаянов вопрос именно в том, что предлагается такое API сделать асинхронным по умолчанию
    • Олексій Вайсруб
      Олексій Вайсруб 4 года назад
      Alexey Rybak "асинхронным по умолчанию" означает что на любой простецкий запрос вместо двух пакетов туда-сюда у вас получается как минимум 4: туда_запрос - обратно_токен - запрос по токену - ответ по токену. Спрашивается, зачем удваивать трафик и нагрузку?
  8. Дмитрий Юркин
    Дмитрий Юркин 4 года назад
    Еще важный плюс для систем, где время ожидания ответа на запрос может быть высоким. Тогда отдаем идентификатор запроса, и потребитель запрашивая ответ по идентификатору может получить статус "ожидание" или сам ответ, в зависимости от готовности. Это снимает риски таймаутов при ожидании быстрого ответа и в целом допускает некоторую деградацию скорости, прежде чем спохватиться и наращивать обслуживающие инстансы. Собственно, выше уже написали, не успел прочесть.
  9. Александр Лурье
    Александр Лурье 4 года назад
    Асинхронное решение сложнее масштабировать. В нем появляется центральная база данных, к которой надо делать consistent write и consistent read на каждую операцию. Если операция связана с изменением данных в центральной базе, то это не добавляет избыточную зависимость, а если нет, то вы получаете новое узкое место в системе.
    • Константин Панфилов
      Константин Панфилов 4 года назад
      Александр Лурье а ещё куча рэйскондишен и локов
  10. Андрей Федоровский
    Андрей Федоровский 4 года назад
    Асинхронные API больше подходят для потоковой и/или неравномерной нагрузки. В них важна общая пропускная способность и гарантия обработки, но в общем, неважна задержка при обработке индивидуальных запросов.Например, хитрый многостадийный конвейер с пакетной обработкой на стороне MQ может значительно поднять скорость потоковой работы, но индивидуальный реквест может в нем затеряться на много секунд.
  11. Андрей Литвиненко
    Андрей Литвиненко 4 года назад
    По таким вводным звучит как усложнение ради того что разрабы хотят попробовать новое
  12. Олексій Вайсруб
    Олексій Вайсруб 4 года назад
    Насколько я разбираюсь в CQRS - это не про синхронность/асинхроннось, а про разделение запросов на модифицирующие (команды) и немодифицирующие(запросы). Так что ничто не мешает CQRS быть как синхронным так и асинхронным.
    • Олексій Вайсруб
      Олексій Вайсруб 4 года назад
      UPD. Происходит это примерно так:Сначала всё идёт хорошо, и даже с ростом нагрузки очередь справлятся. Потом какой-то таск усложняется и время ответа начинает подтормаживать всю очередь, что превышает позволительные задержки, вы начинаете делить очередь на быстрые и медленные таски, потом появляются приоритетные таски, и из них выстраивается своя очередь. Потом появляется внешнее взаимодействие и для него делается отдельная очередь, потом какой-то из внешних клиентов начинает тупить (или отключается) тормозя остальных вместо нескольких милисекунд завершаясь по таймауту (десятки секунд), а следующая задача в очереди тоже его, и вы начинаете выстраивать отдельные очереди на каждого клиента и перекладыывать сообщения из очереди в очередь, потом у вас появлятся "связанные таски" и вы начинаете перекладывать в очереди не только запроы, но и ответы которые надо отдавать вместе, пытаясь реализовать на ваших очередях что-то типа промисов. потом.... и т.д.
  13. Алик Курдюков
    Алик Курдюков 4 года назад
    Я бы так не делал без особой необходимости. Гемора _очень_ много, профит неясен
  14. Михаил Тюрин
    Михаил Тюрин 4 года назад
    Теоретическая рекомендация — переходить на асинк при деградации. Ну а так, давай инструменты — команды выберут . Делать как обязаловку — все таки перебор , хоть и красиво .
  15. Денис Габайдулин
    Денис Габайдулин 4 года назад
    Делать такое как общий механизм на уровне app layer выглядит немного сомнительной идеей, если сразу не говорить о транспортном протоколе и деталях реализации. Потому что от них будет зависеть latency в итоге. И может получится, что синхронный HTTP вызов будет иметь даже более низкую latency.
    • Роман Домрачев
      Роман Домрачев 4 года назад
      Тоже использовал эти варианты.С нативным grpc не подружился, были проблемы с тем, что по крайней мере в гошной имплементации клиент и сервер периодически расходились во мнении о живости коннекта. И в браузере его не было, чтобы использовать активно.С websocket всё неплохо, если пинг-понг не забывать реализовывать и политику ретраев заранее продумать.
  16. Филипп Дельгядо
    Филипп Дельгядо 4 года назад
    А как вы будете server-sent-messages на бэке делать? Там все не очень очевидно, увы. Ну и неудобно в использовании, конечно. И придется решать кучу разнообразных проблем с синхронизацией
  17. Антон Герасимов
    Антон Герасимов 4 года назад
    Если можно сделать api с клиента синхронным — нужно делать синхронным. Плюсы синхронного АПИ во много раз превышают минусы (время разработки, стоимость разработчиков, простота тестирования и т.д)
  18. Dmitry Senchenkov
    Dmitry Senchenkov 4 года назад
    Попробую поделиться скромным опытом на кейсах. Кейс первый. Есть у тебя запрос, под которым может быть не совсем предсказуемая по времени операция. Теоретически долгая операция. Много чего может затрагивать и по тайм-ауту может отвалиться. Тут наверное один из вариантов - асинхронность. Добавляем и боль проходит. Не добавляет - оно взрывается на первом тестировщике, который решает что-то побольше прокачать чем минимальные данные (проверено на практике). Если теоретически может быть очень долго но на практике не получается никак - возможно и не стоит париться. Второй случай. Делаешь системную нагрузку и потом имеешь, что без очередей и опять же асинхронности беда. Захлёбываемся. Масштабировать больно и дорого или вообще потолок и неликвидность накрывает. Делаешь асинхронность. Переделываешь. Настраиваешь. Перенастраиваешь. Неожиданно боль проходит. Потолки пропадают. Всплески нагрузки не страшны. Автомасштабирование поспевает за нагрузкой. И прочие прелести и розовые пони. Но опять же не дёшево в самой разработке и отдадке. По кр мере первый раз. При этом и в первом и во втором кейсе попытка завернуть в синхронщину заканчивается плохо. Очень плохо. И при этом оказывается что в идеале нужно ещё кое-чего по итогам в техдолг положить чтобы работало хорошо и красиво. Кейс третий. Есть у тебя метод, отрабатывающий за миллисекунды и хороший по всем нагрузкам. Ты его делаешь асинхронным ибо так есть модно. Даёшь нагрузку и оказывается что оно работает медленнее, стоит дороже и ресурсов требует существенно больше. Чешешь репу. Откатываешь уже другую репу на состояние "до". Итоговые выводы от кэпа таковы: 1 - решать конкретную боль при её возникновении хорошо, 2 - решать боль которой нет - плохо, 3 - "всё под одну гребёнку" редко работает хорошо, 4 - асинхронность сложнее и дороже и больше подводных камней, но иногда без неё никак или очень дорого, 5 - асинхронность не приносит пользы и даже вредит там, где она не нужна. Рекомендации: воткнуть асинхронность там где она нужна и без неё будет очень больно и это точно, 2 - не втыкать асинхронность там, где она не нужна.
  19. Андрей Ковбович
    Андрей Ковбович 4 года назад
    делали на основе rabbitmq (async by design), поверх которого синхронный/асинхронный xml api endpoint
  20. Anton Okolelov
    Anton Okolelov 4 года назад
    Сложнее разрабатывать, а профит неясен
  21. Дмитрий Горяинов
    Дмитрий Горяинов 4 года назад
    by default отложенное исполнение смущает. Используем и то, и то с очередями и все такое, но... Поведение зависит от службы, которая стоит за конкретной точкой API, а само API это прослойка (обеспечивает авторизацию, запросов, единую спецификацию запросов и ответов, "транспорт" запроса в нужную службу). Как-то так.
  22. Andrey Shetukhin
    Andrey Shetukhin 4 года назад
    Прочитав обоснование, я пришёл к выводу, что разработчикам очень хочется сделать Абсолютно Инновационный Проект, Который Никто До Них Не Делал.
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Andrey Shetukhin я предложил как раз разделить - «партнерам предоставлять традиционный апи, внутри своей системы - попробовать асинхронно (потребитель - броузер). Плюс всегда есть простой способ сделать синхронный враппер поверх асинхронного апи, если никакой другой вариант не подошел. Но проблема шире - под капотом с асинхронным апи идет прямо тру event sourcing вместо традиционной работы с субд и вот это лично меня совсем пугает.
    • Антон Герасимов
      Антон Герасимов 4 года назад
      Andrey Shetukhin : тут скорее вопрос, а почему всю эту асинхронщину не переложить на браузер? Может потому, что эвентлуп там работает всеж в один поток и вся асинхронщина там просто псевдо.
    • Сергей Зомойлов
      Сергей Зомойлов 4 года назад
      Andrey Shetukhin А что делать, если ответ может придти на следующий день? Начинать все равно с синхронного апи?
    • Сергей Зомойлов
      Сергей Зомойлов 4 года назад
      Andrey Shetukhin Гм, ну поставил суточное задание быстренько так, вот тебе и отзывчивость.Вообще асинхронное выполение будет чем дальше тем больше, лучше постепенно начинать к этому привыкать.
    • Oleg Chirukhin
      Oleg Chirukhin 4 года назад
      Andrey Shetukhin асинхронка снимет нагрузку с сервера. За счёт сложности использования апи снаружи. Но снаружи-то использовать будешь не ты, поэтому пох. Помогать кому-либо необязательно, не разобрались - лохи. Купят других разработчиков (не школьников, а нормальных) и разберутся, если припрет.
    • Роман Тимофеев
      Роман Тимофеев 4 года назад
      Andrey Shetukhin
  23. Anton Savelyev
    Anton Savelyev 4 года назад
    Звучит, как гора велосипедов для замены gRPC.
  24. Kirill Vechera
    Kirill Vechera 4 года назад
    Я вижу одним безусловным плюсом асинхронного подхода, то что он масштабируется легче до гиганских масштабов и в целом плотность использования железа у такого подхода выше - не нужно висеть миллиону соединений для того, чтобы ждать завершения. Но это нужно реально быть уверенным, что вы без существенного изменения проекта быстро дорастете до гигантского масштаба.
  25. Илья Анфимов
    Илья Анфимов 4 года назад
    >асинхронность из коробки, значит более отзывчивый интерфейс
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Илья Анфимов постановка в очередь в целом сильно быстрее операция, чем обработка запроса. С точки зрения фронтенд-разработки код для асинхронных апи делается один раз и дальше код пишется так же просто, как и код для синхронного.
    • Роман Тимофеев
      Роман Тимофеев 4 года назад
      Антон Герасимов а как связана борьба с дублями и асинхронное api?
    • Илья Анфимов
      Илья Анфимов 4 года назад
      >раз постановка «да, быстрее» ну значит все-таки отзывчивый.
    • Роман Тимофеев
      Роман Тимофеев 4 года назад
      Илья Анфимов Надо просто не использовать поллинг
    • Илья Анфимов
      Илья Анфимов 4 года назад
      >Надо просто не использовать поллинг
    • Роман Тимофеев
      Роман Тимофеев 4 года назад
      Илья Анфимов
    • Илья Анфимов
      Илья Анфимов 4 года назад
      >Что сложного в TCP?
    • Илья Анфимов
      Илья Анфимов 4 года назад
      >Ещё есть стримы в http/2
  26. Илья Анфимов
    Илья Анфимов 4 года назад
    Далее, постановка задания в очередь с получением идэнтификатора -- это само по себе имеет какие-то затраты времени. Если верхняя оцэнка времени выполнения задания меньшэ верхней оцэнки времени постановки его в очередь -- то это получается очень странное развлечение, пытаться добиться отзывчивости таким способом. Если сопоставимо -- то просто странное.
  27. Павел Хаскель
    Павел Хаскель 4 года назад
    Ничто не мешает сделать реализацию под синхронную и асинхронную модель одновременно. И определять на уровне конфига или интерфейса, что делать с объектом команды|квери после попадания в медиатор: либо сразу в хендлер, либо в очередь.С асинхронщиной неприятно, что появляется куча хранилищ, какие-то воркеры, которые надо как-то обслуживать. Если это не делается как общий подход для всей компании, лучше наверное не делать, чтобы не плодить внутренние невзлетевшие фреймворки.
  28. Эрик Олдманн
    Эрик Олдманн 4 года назад
    Как обеспечивается консистентность? Каковы затраты на обеспечение консистентности? Что будет, если MQ потеряет сообщение?
    • Oleg Chirukhin
      Oleg Chirukhin 4 года назад
      Eric Oldmann клиент должен это ретраить и обеспечивать все за свой счет, конечно
    • Владимир Коркодинов
      Владимир Коркодинов 4 года назад
      Oleg Chirukhin клиент должен страдать
  29. Эрик Олдманн
    Эрик Олдманн 4 года назад
    Мотивацию разработчиков я понимаю, им за такой cloud native будут очень хорошо платить на другом месте работы)
    • Антон Герасимов
      Антон Герасимов 4 года назад
      Эрик Олдманн : а ты думаешь, что на текущем им недоплачивают, но дают делать такой дизайн?
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Эрик Олдманн а почему ты называешь это cloud native?
    • Эрик Олдманн
      Эрик Олдманн 4 года назад
      Anton Gerasimov мне сложно представить масштабы задач, которым требовался бы такой подход. Если вижу вопиюще нерациональные хотелки, включается принцип «qui prodest».
    • Эрик Олдманн
      Эрик Олдманн 4 года назад
      Alexey Rybak потому что системные функции планировщика/распределённого менеджера блокировок вынесены в третью сущность, MQ.
    • Роман Тимофеев
      Роман Тимофеев 4 года назад
      Эрик Олдманн А откуда тут возник MQ? Это же детали реализации, а не протокола.
    • Эрик Олдманн
      Эрик Олдманн 4 года назад
      Alexey Rybak не только лишь многие знают, что алгоритмы менеджера распределённых блокировок есть uber IP. И да, например, IBM MQ использует алгоритм из семейства ARIS, как DB2 и MSSQL.
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Эрик Олдманн эта песенка стара! В 99% случаев можно обойтись без распределенных блокировок.
    • Эрик Олдманн
      Эрик Олдманн 4 года назад
      Роман Тимофеев отлично! Давайте и очередь сообщений свою напишем!
    • Роман Тимофеев
      Роман Тимофеев 4 года назад
      Эрик Олдманн Зачем?
    • Эрик Олдманн
      Эрик Олдманн 4 года назад
      Роман Тимофеев затем, что у нас масштабируемое решение, а не костыли с долбёжкой в батч.
    • Роман Тимофеев
      Роман Тимофеев 4 года назад
      Эрик Олдманн Что такое "долбёжка в батч"?
  30. Oleg Chirukhin
    Oleg Chirukhin 4 года назад
    Это хороший способ сделать систему, которая будет выдерживать высокие нагрузки и масштабироваться. Поддержу ваших разработчиков!
    • Alexey Rybak
      Alexey Rybak 4 года назад
      Oleg Chirukhin мы тут все немного архитекторы, но архитекторы с разным опытом и разными ценностями. Ваш комментарий о том, что «можно» понятен, у меня вопрос: Вы сами строили что-то подобное и эксплуатировали или нет?
    • Oleg Chirukhin
      Oleg Chirukhin 4 года назад
      Alexey Rybak ну типа есть архитектор *вашего* проекта, и есть все остальные люди на земле, пусть у них там миллион пядей во лбу и три нобелевки вопрос в том, что экономически выгодно лично вам как организации, где конкретно утекает бабло и как эти утечки можно списать на кого-то другого Если Бабло не утекает на то, чтобы своими силами делать сервера, которые умеют асинхронные запросы - ну делайте, чо Как верно заметил Олдмен, туда же можно списать не только асинхронность, а ещё и консистетность, или даже географическую целостность (обратился на ближайший сервер, а там ничего нет - ищи асинхронно теперь в сотне серверов по всему миру) - все это требует вычислительных и человеческих ресурсов на поддержку.
    • Филипп Дельгядо
      Филипп Дельгядо 4 года назад
      Oleg Chirukhin, кстати, а с чего бы асинхронной системе масштабироваться лучше, чем синхронной? Ведь нужно будет как-то реализовывать нотификации с сервера на клиент, нужно будет где-то хранить сформированные ответы до того, как их заберут - все это масштабируется гораздо хуже, нежели тривиальные синхронные вызовы с повтором.
  31. Oleg Chirukhin
    Oleg Chirukhin 4 года назад
    Что касается отзывчивости пользовательского интерфейса... у меня на практике было всегда даже наоборот - вот именно в пользовательском интерфейсе это всегда означало дичайшие лаги. Ну просто потому, что у пользовательского UI и очереди трейдоыы сделан в разные стороны. Пользователь хочет получить ответ на нажатие кнопки мгновенно, а очередь хочет вернуть данные когда нужно ей. Если нажатие кнопки ставит запрос в очередь - и пользователь продолжает жать и жать кнопку и ожидать ответа - то в очереди скапливаются сообщения. Чем больше их скапливается, тем больше их протухает по таймауту. Откуда взялся тайм-аут - ну, пользователь же не хочет ждать ответа вечно, через минуту ответ на нажатие кнопки больше не актуален. Образуется паразитная положительная обратная связь. У пользователя «интерфейс лагает». Так что это опять чисто экономический вопрос - готовы ли мы наши сэкономленные деньги превратить в страдания пользователей. Наверное, ну если очень сильно экономим - то да. А иначе синхронные запросы с гарантированным временем ответа куда приятней
  32. Илья Кондрашов
    Илья Кондрашов 4 года назад
    Около 3х лет жил с промежуточным решением: api был синхронным, на бэкэнде любое действие пользователя заворачивалось в команду (с предварительно сгенерированым uuid если что-то предполагалось создать). Команда выполнялась синхронно, не возвращала ничего но могла упасть с исключением. Данные для ответа выбирались заново. Случаев когда нужна была асинхронность были действительно единичные, решал каждый раз по-своему на стороне клиента: чаще всего делал несколько попыток запросить данные по uuid с интервалом в секунду - если система не упала этого хватало в 99.9% случаях.
  33. Роман Тимофеев
    Роман Тимофеев 4 года назад
    Идея хорошая и правильная, но реализация может все убить. Чтобы этого не произошло, надо иметь постоянный двунаправленный канал обмена сообщениями с сервером и batch submission.