(java || kotlin) && devOps
369 subscribers
6 photos
1 video
6 files
306 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

Что такое REST? Чтобы не увеличивать энтропию в сети я дам ссылку на хорошую статью про REST - https://habr.com/ru/post/590679/
Но как всегда попробую сделать из нее краткие выводы)))

Основные заблуждения касаемо REST:
1) REST - это не протокол, а парадигма при создании API. Большинство требований REST не относятся к протоколу (HTTP)
2) JSON не является обязательным для REST, просто большинство REST API используют JSON
3) нельзя сказать, что если мы не используем HTTP глаголы или продумали концепцию REST ресурсов - это не REST. Есть 4 уровня зрелости REST, https://habrastorage.org/getpro/habr/upload_files/bd8/e4b/848/bd8e4b8488e0085b7404a37dc93faf6c.png Выставляя API в виде набора HTTP POST запросов, мы находимся на 1-м уровне зрелости. Добавляем концепцию ресурсов, выраженных в HTTP path - 2-й уровень. HTTP глаголы - 3-й уровень. А 4-й - это такая интересная штука, как HATEOS. Детали: https://habr.com/ru/post/483328/ Кто-то его видел в живой природе, кстати?
4) REST не означает, что про валидацию по схеме можно забыть. Тот же OpenAPI - очень хорош, и позволяет достаточно гибко настроить схему и описать протокол взаимодействия.

Важные замечания:
1) REST по определению должен быть stateless. Как это понимать? Я скажу так: если мы привязываем клиента к конкретному серверу - это уже не stateless. Что значит привязываем? Это значит мы закэшировали что-то важное на этом конкретном сервере. И при выходе из строя этого сервера текущий процесс клиента ломается, например, ему приходится перелогинится или он потеряет введенные данные. Т.е. использование sticky session для оптимизации - не отменяет REST.
2) stateless не значит хранить все на клиенте - в куках или в кэше МП. Куки, к слову, вообще не предназначены для хранения больших объемов данных, т.к. передаются при каждом запросе и неустойчивы к взлому. Кроме того, хранение данных на клиенте чувствительно к его сбоям, не омниканально и усложняет процесс обновления клиента, т.к. приходится обновлять и его кэш. Хранить состояние нужно либо в БД, либо в расределенном кэше
3) stateless критически важно не потому, что идеолог REST написал так в своей статье) stateless дает нам легкое горизонтальное масштабирование с помощью k8s
4) не везде об этом говорится, но исходя из идеи прозрачности REST - по возможности стоит переиспользовать коды ошибок протокола HTTP. Их кстати довольно много https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP Хотя все равно может не хватить в вашем конкретном случае, тогда можно расширить список кодов через body

#REST #API #http
Всем привет!

В протоколе HTTP, а также gRPC, основанном на HTTP/2, есть возможность передачи параметров запроса разными способами:
1) в заголовках
2) в параметрах
3) в path части url
4) в теле запроса

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

1) headers - не бизнесовые, технические данные. Примеры: id для tracing, для логирования, маршрутизации, токены\куки для авторизации. Ключевой момент - в бизнес-процессе эти данные не используются
2) params - как правило используются в GET запросах, по сути фильтр для выборки данных
3) path - как правило в REST запросе, построенном на концепции REST ресурсов, для идентификации ресурса: GET\PUT\DELETE .../order/{nnn}
4) body - атрибуты объекта для создания\обновления в REST. Любая закрытая информация - https шифрует заголовки и тело запроса, но само собой не шифрует параметры и путь. Большие объемы данных, в URL есть ограничения на длину, причем в разных браузерах и проксях они разные, лучше к ним не приближаться). По объему данных в заголовках также есть ограничения на веб-серверах, привет "413 Entity Too Large". Размер можно подкрутить в большую сторону, но опять же это не наш метод)

#rest #api
Всем привет!

Во-первых поздравляю всех с Днем Победы!

Но совсем без разработки в посте я не могу)
Поэтому вернемся к разговору про API.
Давно хотел написать про важность идемпотентности API, о которой упоминал в https://t.me/javaKotlinDevOps/24
Но меня опередили https://habr.com/ru/companies/yandex/articles/442762/
Причем добавить по большому счету нечего, кроме пары замечаний.
Первое - лучше не пытаться решить проблему повторных заказов и таймаутов на клиентской стороне.
Основные проверки должны быть на сервере, на клиенте - дополнительные, улучшающие клиентский опыт.
Ну и второй момент - правильность реализации идемпотентности невозможно проверить автоматически, достаточно сложно - автотестами. Очень важен процесс код-ревью.

#api
Всем привет!

Я упоминал в посте про JMS vs Kafka про ESB - Enterprise Service Bus. Она же Корпоративная Сервисная Шина.
Какие плюсы и минусы данного решения?

Плюсов я вижу три:
1) унификация API в пределах компании
2) единая точка мониторинга и контроля всех интеграций
3) больше возможностей для переиспользования уже существующих API

Минусы такие:
1) т.к. ESB - это отдельная система, то ее разрабатывает как правило отдельная команда, которая быстро становится узким местом. Особенно при внедрении микросервисной архитектуры и резком увеличении числа интеграций
2) ESB как правило вносит дополнительные задержки, что особенно критично при синхронном взаимодействии
3) как правило ESB предлагают коммерческие компании, что приводит к vendor lock. Слезть с такого решения будет сложно
4) неочевидная штука - с одной стороны команда ESB унифицирует все API. Но с другой - API становятся перегруженными. Там будут какие-то стандартизированные для всех поля, общие базовые типы, которые во многих случаях будут избыточными
5) также ESB из-за стандартизации затрудняет развитие API. Т.к. унификация, а кроме того больше команд участвует в согласовании - три вместо двух

Вывод: на данном этапе развития ПО идея ESB выглядит избыточной.

Но что же с плюсами, как добиться того же результата с API точка-точка и микросервисной архитектурой?
1) унификация вещь полезная, главное чтобы она не была избыточной. Архитектор может выставить требования по API, DevOps - встроить их проверку в pipeline.
2) единая точка контроля - в случае облачной среды такой точкой может быть Istio или k8s. Либо ставить proxy на границах сред.
3) для переиспользования можно использовать каталог API. Да и не всегда переиспользование полезно, см. выше про избыточное API. Также отдельное API для каждого потребителя позволяет лучше контролировать доступ к данным

#api #esb
Всем привет!

Прочитал статью о том, как можно обойтись без OpenAPI при взаимодействии по REST API https://habr.com/ru/companies/magnit/articles/763952
Для тех кому лень читать - там предлагается использовать Java DTO. В целом подход интересный и рабочий. Но есть нюанс. Схема OpenAPI - внешний артефакт. Он лежит либо в git репозитории или API Studio от Swagger. Код по нему генерируется в каталоге сборки. Забыть обновится при выходе новой версии сложнее как раз из-за чёткого понимания того, что API - внешнее. Хотя конечно же можно )
Другое дело Java API. Это про сути ещё одна из десятков библиотек, подключенных к проекту. Версию библиотеки мы фиксируем как это принято при управлении зависимостями. Забыть о том, что это внешнее API, гораздо легче.
Я сейчас говорю не только про кейс, описанный в статье. Ещё есть вариант обмена данными через распределенный кэш, путём сериализации Java POJO объектов, встречал его на практике. А самый яркий антипаттерн при похожем подходе - обмен данными через БД. В этом случае как правило обмениваются скриптами БД, а не классами. Но идея похожа - зачем нам лишняя сущность в виде схемы, лишние преобразования, когда уже есть код. Или уже есть таблица в БД. Конечно, интеграция через БД стала антипаттерном в том числе и потому, что БД - это не только схема, но ещё и транзакции, блокировки, триггеры. Но главную причину я озвучил выше - каждая команда будет считать базу своей внутренней, забывая, что это API. Проблема психологическая, не техническая.
Итого: подход интересный, имеет права на жизнь (я про обмен jar-никами, не про интеграцию через БД), но требует дисциплины. Или автоматизации на этапе CI, позволяющей не надеятся на человеческий фактор.

P.S. Как говорится, не OpenAPI единым - есть ещё Protobuf, GraphQL, xsd наконец. Но самый распространённый - OpenAPI, поэтому везде упоминается он.

#api #OpenAPI #integration
Всем привет!

Наверное вы слышали про такой стандарт API как GraphQL. Если посмотреть на главную страницу их сайта https://graphql.org/, то там бросается в глаза фраза: "Evolve your API without versions".
Если воспринимать ее буквально, то можно подумать: "О, круто, API без версионирования! А что так можно было?)"

Но как это обычно бывает в разработке - не все так просто.

Если подумать - API без версионирования и без сопутствующих ему проблем, в частности несовместимых изменений, может быть только в том случае, если в API вообще нет обязательных полей. Но полезность такого API стремится к нулю. А точнее это уже не API, а что-то похоже на поиск Google или Yandex или общение с ChatGPT.
В GraphQL конечно же обязательные поля есть. Единственный момент - по умолчанию все поля не обязательны.

А если подумать еще - есть еще один способ. Завязаться на какой-то стандарт уровня HTTP, HTML или SWIFT, у которого так много потребителей, что волей неволей придется поддерживать обратную совместимость. Матерится сквозь зубы, но поддерживать) И то, даже в таких областях появляются новые версии API.
Плюс можно добавить уровень API шлюза - наружу выдаем API по стандарт, внутри - свое, которое может меняться свободно.
Но напрямую к GraphQL этот кейс не относится, это можно сделать и с REST, и с JSON-RPC.

Но если вчитаться в документацию GraphQL внимательнее, то видно, что авторы предлагают другое - эволюцию API мелкими шагами без явного задания версий.
https://principledgraphql.com/agility/#5-use-an-agile-approach-to-schema-development
Agile в деле построения API.
По сути это старый добрый принцип:
1) добавляем новый метод в API
2) старый объявляем deprecated
3) уведомляем об этом потребителей, не забывая про дату вывода из эксплуатации
4) когда придет время удаляем старое API
Profit!

В GraphQL выглядит это вот так:

type Account {
surname: String! @deprecated(reason: "Use personSurname")
personSurname: String
}

В чем тут могут быть проблемы:
1) не факт, что потребители вовремя обновятся. Да, deprecated на уровне API лучше, чем рассылка потребителям или страничка в сети, но это не панацея. К слову, OpenAPI тоже так умеет.
2) да, контролировать потребителей на уровне отдельного deprecated поля в GraphQL гораздо проще: возможность на клиенте указывать только нужные поля - это наверное главная фишка GraphQL. Но не все потребители будут так делать. Некоторые - сознательно, нарушая конвенцию использования GraphQL, некоторые - случайно, просто забыв убрать лишние поля
3) если работать до последнего потребителя - API быстро превратится в помойку
4) а главное: если заводить множество мелких изменений в API - каждый спринт по изменению - со временем очень сложно станет этим управлять. Да, есть инструменты, облегчающие управление - https://github.com/kamilkisiela/graphql-inspector Да, нужна четкая политика по работе с изменениями API. Но у меня есть сомнения, будет ли это все работать при непрерывном потоке изменений в большой организации без введения новых версий. Как и Agile в целом такой подход требует ответственности от команды и также плохо масштабируется.

Ну и последнее. Да, ничего для создания версий в GraphQL нет. Но никто не мешает развернуть рядом endpoint и назвать его graphql/v2. Или в схему к полю xyz добавить поле xyzV2 )))

#api #graphql #versioning
Всем привет!

Самая крутая фича GraphQL - это возможность явного указания какие данные нужны клиенту.
Причем фильтровать можно на любом уровне вложенности объектов.
И к тому же фильтровать не только по возвращаемым полям, но и по списку записей - аналог WHERE в SQL.
Пример запроса:

{
authors(surname: "Пушкин") {
name,
surname,
books(year: "1831")) {
title,
genre,
year
}
}
}

На стороне сервера для каждого объекта в запросе - в моем примере Author и Book - можно написать свой fetcher, независимо вытаскивающий данные из хранилища.
А вишенка на торте - с помощью Federation https://netflix.github.io/dgs/advanced/federated-testing/ можно децентрализовать схему, расположив fetchers в разных микросервисах.

Но главный плюс также является и главной потенциальной проблемой.
GraphQL подталкивает к тому, чтобы был один endpoint, через который можно получить все данные.
А если потребители захотят получить все?)

Встают вопросы:
1) разграничения доступа. В целом решается интеграцией со Spring Security https://netflix.github.io/dgs/advanced/security/ Можно расставить аннотации @Secured над каждым объектом. Но я вижу больше рисков по сравнению с специальным REST endpoint, "заточенным" под определенный набор данных.
2) производительности - ведь одним запросом к GraphQL сервису в теории можно вытащить все содержимое БД. А скорее получить проблему N+1 и подвесить БД). Авторы естественно знают об этой проблеме https://netflix.github.io/dgs/data-loaders/ и предлагают решение: batching и кэширование. Но решение не коробочное, требуется проектирование

P.S. Еще одна крутая фича - graphiql - интерактивная веб-консоль для построения запросов, включенная по умолчанию https://github.com/graphql/graphiql C auto-completion.
Ну и конечно возможность подписки на события как часть протокола GraphQL. Реализуется как правило через WebsSocket https://netflix.github.io/dgs/advanced/subscriptions/

#graphql #api
Примерный набор команд:
git config submodule.recurse true # добавляет ключ --recurse-submodules ко всем командам кроме clone. В частности checkout и pull
git config diff.submodule log # отображать также изменения в подмодулях при выполнении diff в корне
git config alias.clones 'clone --recurse-submodules' # подтягивать подмодули при клонировании
git config alias.diffs '!'"git diff && git submodule foreach 'git diff'" # показать изменения по всем подмодулям
git config alias.pushs 'push --recurse-submodules=on-demand' # запушить изменения вначале в подмодулях, потом в основной проект
git config alias.updates 'submodule update --remote --merge' # подтянуть изменения подмодулей из удаленных репозиториев и влить их в локальную копию. После этого можно самому вносить изменения в подмодуль

В любом случае предлагаю прочитать статью по ссылке выше.

#git #api #integraion
Качественное ли у вас API? А чем докажете?)

Как мы проверяем код на качество? SonarQube, покрытие кода тестами. Если говорить о code style - CheckStyle-ом. Если говорить об уязвимостях - проверка по базам уязвимостей (разные тулы), Checkmarx.

А можно ли как-то проверить API на соответствие лучшим практикам? В частности, OpenAPI как самый типовой на данный момент вариант.
Да - для этого есть Spectral linter https://meta.stoplight.io/docs/spectral/a630feff97e3a-concepts

У него три основных достоинства:
1) это linter и его можно включить в CI pipeline

2) у него есть наборы предустановленных правил, в частности:
а) OpenAPI rules https://meta.stoplight.io/docs/spectral/4dec24461f3af-open-api-rules
б) URL rules https://apistylebook.stoplight.io/docs/url-guidelines - использование kebab-case, не использование get в URL...
в) OWASP rules https://apistylebook.stoplight.io/docs/owasp-top-10 - безопасность, например, использование uuid вместо чисел в идентификаторах
...

3) возможность добавлять свои правила https://meta.stoplight.io/docs/spectral/01baf06bdd05a-create-a-ruleset в том числе наследуясь от существующих

Ну и отдельно отмечу, что есть плагин для IDEA https://plugins.jetbrains.com/plugin/25989-spectral-linter

Итого - штука полезная, настоятельно рекомендую попробовать.

#api #arch #code_quality
OpenAPI для асинхрона - а, пожалуйста

Я думаю все знают, как описать API для REST взаимодействия. OpenAPI давно уже стал стандартом.

Перечислю его основные плюсы:
1) широкое распространение
2) автоматическая генерация документации
3) онлайн редактор Swagger UI
4) генерация серверного и клиентского кода по схеме
5) стандарт покрывает не только собственно содержимое body сообщения, но и описывает всю схему взаимодействия: параметры URL, заголовки включая куки, коды ошибок. Даже адреса серверов, хотя как по мне - это спорный момент.

Но асинхронное взаимодействие, ака Event-Driven Architecture: Kafka, MQ.. - имеют свои особенности. Как минимум это:
1) нет понятия запрос-ответ, вместо этого обмен идет сообщениями
2) другой набор протоколов (хотя http есть и там, и там)
3) другая терминология - есть каналы (топик или очередь), у канала есть свои характеристики
4) в любом взаимодействии нужно описать 3 участников: producer, канал, consumer.

Поэтому появился AsyncAPI. Он достаточно сильно похож на OpenAPI:
1) тот же YAML,
2) похожи (с точностью до сказанного выше) основные блоки, из которых строится API - Info, Servers, Tags, Schemas...
Видно чем вдохновлялись авторы) Как сказано на официальном сайте: "Part of this content has been taken from the great work done by the folks at the OpenAPI Initiative."
И это хорошо.

Стандарт молодой, но уже стал стандартом ))) да, тавтология) Имеет все те же преимущества, что и OpenAPI - см. начало поста.
Вот неплохая статья с примером https://bigdataschool.ru/blog/asyncapi-specification-for-kafka-practical-example.html

Поэтому настоятельно рекомендую для использования.

#api #openapi #asyncapi