Как стать автором
Обновить

Комментарии 26

API, за которое потом не будет стыдно.

<grammar-nazi

API – он. Как кофе.

grammar-nazi/>

API-лки, они! (c) Осёл из м/ф "Шрек".jpg

Так то оно так, но существительное единственного числа оканчивающееся на и интуитивно воспринимается как слово среднего рода: алиби, жюри, пари, селфи, такси. И это-ж неспроста!

Алибий, жюрий, парий, селфий, таксий.

Страшно представить, как вы слово FAQ произносите.

Голосом Т-800.

Ну блин! Я тоже хотел написать про кофе:

API -- он как кофе: либо хороший, либо оно.

:)))

Хороший API. Все же мужского рода.

ответ GET-запроса по умолчанию кешируется.

По умолчанию он не кешируется. Если так, то у вас проблема настроек. Кеширование любых запросов - управляемая вещь в вашем сервисе и API шлюзе.

Поэтому если хотите чтобы вашим API пользовалась максимально широкая аудитория с минимальной головной болью — создавайте выкладывайте клиентские библиотеки.

По этому, документацию делайте и выкладывайте в формате OpenAPI (ну можно туда еще swagger ui привинтить). Вы не покроете все языки своими библиотеками (вы с питона на шарпы как? Норм? А на go? И ребятам в perl отсыпте.) да и иногда нужно от вашей API 2-3 метода а тащить туда библу, да еще вопрос на сколько в ней код кривой или не подходящий (пример таймауты которые вы забыли дать пользователю)? Но да, хорошо иметь библу.

Вполне может быть и каскадный сбой, когда из‑за отключения одного сервиса вылетает десяток других по цепочке вызова. Чтобы свести к минимуму возможные последствия, мы реализуем переключатель, при активации которого вместо реальных данных отдаются заглушки — пустые структуры, а сам признак «API отключено» отдается с помощью числового кода (см. выше).

Это проблема микросервисной архитектуры by design. Ошибка должна быть тут и все.
Какие пустые структуры если к примеру умер сервис биллинга. Вы все равно ничего сделать не можете, что вы там клиенту выдавать будете? Что вы ему пустые структуры - обман выдавать будете или он реально ваши коды ошибок нормально обработает. Просто выдать ошибку что временно не доступен (не помню код http). Ну или 500.

Кеширование любых запросов - управляемая вещь в вашем сервисе и API шлюзе. 

Только к сожалению декларативная, вы просто значениями заголовков отдаете просьбу не кешировать совсем либо хранить заданное время. Будет ли эта просьба прочитана и учтена на клиенте - вам неведомо и не подконтрольно.

документацию делайте и выкладывайте в формате OpenAPI 

Чтобы лучше понимать о чем вообще речь, посмотрите сначала на это: https://developers.avito.ru/api-catalog

Во-первых документацию традиционно не читают. Во-вторых ведение и сопровождение документации это работа, вполне сопоставимая по объему и сложности с реализацией клиентов.

Еще есть третья важная причина, о которой я сразу забыл упомянуть: любая разработка стоит денег, это затраты.

Поэтому при наличии одного только API, затраты на создание клиента к нему лягут на пользователей. Так что при возможности выбора - выберут готовое решение, просто экономически.

Вы не покроете все языки своими библиотеками

Все и не надо, по классике это Java + Node.js + Python + .NET, временами добавляется PHP. Может быть специфика бизнеса и тогда добавляется реализация в виде плагина для облачной CRM аля Salesforce.

К какому-нибудь Rust с Хаскеллом конечно же никто в своем уме клиентские библиотеки делать не станет.

Это проблема микросервисной архитектуры by design. 

Пока платят вам за решение - проблема ваша. Так это работает.

если к примеру умер сервис биллинга

Тут все сильлно зависит от возможности повторной отправки при сбое. Если такое возможно по условиям обслуживания - входящие сообщения, не дошедшие до биллинга будут накапливаться, клиенту будет отображаться статус "ожидание".

Если биллинг упал и быстро поднялся - накопленные сообщения полетят внутрь и транзакция отработает, если нет - будет отказ по таймауту.

Это в кратце.

Вызов удаленного REST-метода в цикле на пару тысяч итераций — ныне норма и асинхронное API это лучший способ от подобного не страдать.

Гм, нет, не норма и не гарантированный. Что мешает юзеру вызвать апи в цикле два миллиона раз или двадцать? Подобная реализация этого не выдержит. Простой async, это не более чем костыль, в попытке заменить архитектурное решение. Делать каждую функцию асинк - это прямой путь в ад как для создателя апи, так и для его пользователя и без веских причин смысла не имеет. (так и представил матюки фронтэндера, когда я ему предложу для проверки того, что смена имени юзера прошла успешно, вызывать еще один метод в цикле, пока он не вернет "успешно".... )

Если нужна защита от ddos или глупости пользователя, можно на nginx настроить очередь запросов с лимитом по сессии. Если же процесс реально асинхронный, то нужны очереди уже в самом приложении с возможностью не только получить результат, но и отменить операцию (пример: у большинства банков функция приема платежа сейчас асинхронная. Причем стало так с момента, когда появились подтверждения платежей через смс. И вот там действительно никак по другому) Кроме того в таком случае неплохо бы еще и обеспечить возможность для клиента получить колбэк с результатом... Короче это целая система нужна ради каждого вызова. Проще уж тогда использовать вообще не http, а протокол в котором это поддерживается из коробки или хотя бы легче сделать (опять же систему очередей или rpc)

Если проблема в производительности, то тут тоже асинк - это так себе идея. Оно не контролируемо в нужной степени. Как определить, насколько близко отказ системы и при какой нагрузке оно откажет? Никак. То что у большинства это улучшает ситуацию, так это потому, что у них просто нет такой нагрузки, когда происходит отказ. Раньше вывалится база.

При этом пример со статистикой разумный, (только тогда уж ее вообще нужно считать в отдельном процессе на отдельном хосте), но вот делать запись асинхронной имеет смысл только там где это крайне необходимо и на другие варианты не выделяют времени/ресурсов.

С остальными "полезными советами" я оставляю другим спорить. По мне и отказ от кодов http и передача ВЕЗДЕ dto - как минимум спорные решения.

Гм, нет, не норма и не гарантированный. Что мешает юзеру вызвать апи в цикле два миллиона раз или двадцать? Подобная реализация этого не выдержит. 

Тут стоит понимать разницу между внутренним состоянием сервиса (тот же механизм транзакций) и внешним.

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

Также есть регламенты на пороговое количество запросов в сутки, в минуту и тд, которые обычно прописываются в SLA. Если кто-то из пользователей начинает описываемую "долбильню" - доступ приостанавливается по нарушению SLA.

Как определить, насколько близко отказ системы и при какой нагрузке оно откажет?

Не поверите - тестами производительности. Как и во всех других случаях.

но вот делать запись асинхронной 

Дело в теом что в большинстве случаев запись происходит в базу, с обязательным открытием транзакции. Процесс записи более ресурсоемкий чем процесс выборки, о чем любители DDoS прекрасно осведомлены и для отказа системы долбят именно в те места где происходит сложная запись.

Например оформление заказа в интернет-магазине или кнопка "Отправить" тут на Хабре.

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

Ну так и я об этом! А раз вы срежете ддос-еров простым конфигурированием nginx, зачем для этой цели асинхронный код? Если базы нет, или она изначально задумывалась как асинхронная, или там набор вызовов других сервисов, то да, от этого может быть толк. А при работе с обычной базой ускорения не юудет, все упрется в нее. За то будет офигенное усложнение и самого кода и его отладки на пустом месте.

Не поверите - тестами производительности

По мне так крайне не надежный метод с учетом того, что есть куча факторрв, влияющих на производительность прямо в рантайм. Та же реляционная база очень слабо предсказуема. Там возникнет какой то временный затык и это подожит всю цепочку асинхронных приложений. В идеале приложение должно реализовать бэк-преша и сообщать сколько еще запросов можно поставить в очередь. Делать это для каждого апи - явный оверхед.

Дело в теом что в большинстве случаев запись происходит в базу, с обязательным открытием транзакции. Процесс записи более ресурсоемкий чем процесс выборки, о чем любители DDoS прекрасно осведомлены и для отказа системы долбят именно в те места где происходит сложная запись.

Ну так сделайте систему банов - один и тот же юзер тыкает "оформить заказ" 10 раз в секунду? Это бот, баним аккаунт до дальнейших разборок. На входе, если это интернет магазин, с него попросят номер телефона. Да, есть сервисы, которые их выдают, но учитывая, что один номер - один аккаунт, ддос-ер разориться быстрее, чем упадет сервис.

так и представил матюки фронтэндера, когда я ему предложу для проверки того, что смена имени юзера прошла успешно, вызывать еще один метод в цикле, пока он не вернет "успешно"....

Да, мне тоже глаз резануло. Асинхрон хорош в сочетании с реактивностью, или когда ответ вообще не интересен (логи, статистика...). А позиционировать решение, подразумевающее как минимум двукратное (а то и трёх-пятикратное) увеличение количества запросов, как средство повышения производительности системы – ну как минимум, спорно.

А позиционировать решение, подразумевающее как минимум двукратное (а то и трёх-пятикратное) увеличение количества запросов

Не путайте внутреннюю часть системы — общение между фронтэндом и бекэндом и публичное API, это разные вещи.

Какая разница? Там или тут, требование делать API исключительно асинхронным подразумевает появление запросов для считывания результата. Соответственно, как минимум удвоение их количества. И кстати уж, не припомню, чтобы в статье оговаривались рамки применимости публичный/внутренний.

 И кстати уж, не припомню, чтобы в статье оговаривались рамки применимости публичный/внутренний.

Не знаю как это лучше обозвать, «внутреннее API» — так себе, не отражает суть.

По идее вообще не стоит воспринимать взаимодействие внутри одной системы как работу через API, поскольку это часть одного целого.

Нет смысла его описывать, нет смысла заморачиваться обратной совместимостью или какой‑то специальной защитой, нет смысла ограничивать себя работой через DTO — поскольку клиент для такого API поддается изменению в рамках одного и того же релиза что и само API.

Как-то так.

Не понял, при чём тут это вообще. Речь была о том, что требование повсеместной асинхронности API скорее приведёт к деградации производительности, нежели к её повышению – не говоря уж о том, что во многих случаях это тупо неудобно. По этому вопросу есть, что возразить?

 По этому вопросу есть, что возразить?

В таком стиле вам лучше с кем-то другим общаться.

Так я вроде не Вам отвечал. И когда писал, рассчитывал на ответ (если он будет) по обсуждаемой теме. Которой и призываю придерживаться – по-моему, как раз уведение дискуссии в сторону и есть не лучший стиль.

"По идее вообще не стоит воспринимать взаимодействие внутри одной системы как работу через API"

Ну приехали! Нет уж, если это разделено на отдельные сервисы, то формальный интерфейс обязателен и не факт, что эти части вместе будут релизиться. А если одна из частей фронтэнд - так тем более. Ее делает другой человек/команда и они вам за такой подход попытаются открутить какую-нибудь выступающую часть. Возможно вы просто не работали в таких проектах, но поверьте, так и будет ))

Для внутренних запросов в таком случае проще очередь использовать. Она асинхронна сразу, без костылей. Более того - задачи в очереди лучше мониторятся и даже менеджерятся. Их можно спокойно удалить, не выполняя.

"Синхронное API — одна из ключевых причин низкой производительности вебсервисов."
Вообще странное утверждение потому что синхронный вызовы по умолчанию быстрее асинхронных, т.к. не используются очереди, дополнительные потоки и т.д.

А насчет "Что произойдет если появится необходимость добавить новый параметр?" у RequestParam есть проперть defaultValue которую всегда можно выставить

Если честно, до конца не дочитал, наверняка еще найдется пара/трока спорных утверждений

потому что синхронный вызовы по умолчанию быстрее асинхронных

Также как и один сервер будет всегда быстрее кластера, просто потому что нет затрат на сетевое взаимодействие.

которую всегда можно выставить

Это был лишь пример нарушения сигнатуры метода, DTO такие риски сокращает.

один сервер НЕ всегда будет быстрее кластера, потому что у него есть потолок производительности, у кластера его нет (ну или почти нет :)), это называется горизонтальное масштабирование
Насчет DTO, их имеет смысл использовать, когда сигнатруа метода раздувается до 5 и более параметров, это никак не связано с Rest API, такого правила желательно придерживаться в любом API

Насчет DTO, их имеет смысл использовать, когда сигнатруа метода раздувается до 5 и более параметров

В том и смысл что с DTO вы не привязаны к сигнатуре, она не меняется между версиями DTO. Пять параметров, десять или один - становится абсолютно неважно.

Как мне кажется категоричные суждения часто приводят к неверным решением. В данном случае строгий запрет на реализацию синхронных взаимодействий может привести к множеству оверхедов.

Всё-таки применение практик должно основываться на конкретной ситуации, а посыл "не делайте никогда" делает его независимым от контекста ситуации.

А по факту понять хороший или плохой API можно только опытным путем. Т.е. ретроспективно мы оцениваем реализацию и говорим - кажется это хорошая штука. Потому что определять "хорошесть" будут клиенты, которых на этапе проектирования может и не быть.

У вас в примере кода про асинхронность не будет ни асинхронности, ни транзакционности.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории