Всем привет!
Поговорим про микросервисы. Статей будет несколько, от плюсов и минусов до саги и прочих паттернов.
Для начала что такое микросервис. Я дам определение в виде набора обязательных признаков. Обязательны все из них.
1) одна команда
2) отдельная кодовая база
3) отдельная БД
4) отдельный pipeline
5) отдельный релизный цикл
6) небольшой объем кода
7) взаимодействие с другими сервисами через межпроцессное API. REST, gRPC, GraphQL, Kafka - это то, что сейчас на слуху, на самом деле технологий больше.
Для начала рассмотрим плюсы:
1) один сервис разрабатывает одна команда. При этом одна команда может делать несколько приложений, но главное здесь - у одного приложения один хозяин, определяющий вектор его развития. Такие ключевые решения, как: язык, на котором написан код, используемые библиотеки, утилита для сборки и архитектура в конце концов) Проще договорится, меньше согласований и зависимостей от внешних людей или команд. Этого же результата помогает достичь отдельный релизный цикл. Хотя в случае enterprise микросервисов зависимостей не избежать и это боль( Еще одно замечание - микросервис может релизиться независимо при условии использования фича-тогглов https://martinfowler.com/articles/feature-toggles.html и поддержки совместимости API. Альтернатива фича тогглов - писать код так, чтобы его неготовый кусок невозможно было вызывать снаружи до момента полной готовности вас и смежников.
2) МИКРОсервис означает, что кода не должно быть много. Сколько тысяч строк - явно не миллион и не сто тысяч, скорее несколько тысяч. А раз кода не много - в него проще погрузится. Меньше кривая обучения для новых членов команды. А если ты в целом понимаешь как работает приложение - с большей вероятностью доработки и рефакторинг будут сохранять его исходную архитектуру или менять ее единообразно. Как результат сервис будет оставаться понятным и далее.
3) проект быстрее компилируется и запускается, быстрее прогоняются тесты - быстрее обратная связь, быстрее процесс разработки. Проще делать маленькие Pull Request, ревьюверы меньше матерятся, т.к. не видят 100+ файлов в Pull Request, проще слияния. Вообще писать код нужно так, чтобы merge конфликтов не было, но это идеал, к которому надо стремится)
4) DevOps инструменты и практики будут работать "из коробки". На проектах с сотнями тысяч строк pipeline будет падать. И падать достаточно часто. SonarQube не может определить дельту Pull Request и падает. Checkmarx нужно дорабатывать у вендора, чтобы не падал. Джобы сборки оптимизировать, параллелить, чтобы добиться приемлемого времени сборки. Интеграционные тесты падают, т.к. контекст, необходимый для поднятия приложения, постоянно растет. База становится такой сложной, что DBA наотрез отказываются от Liquibase и накатывают скрипты руками пошагово. Число интеграций такое, что набор примочных тестов запущенный автоматически с большой вероятностью упадет и встанет вопрос - а точно нам нужны автотесты? НТшники работают по ночам. Bitbucket дико тормозит, когда размер репозитория превышает 2 Тб, а это вполне реально - ведь хранится вся история изменений. Да и в принципе по статистике чем дольше сборка и deploy, тем больше вероятность, что на jenkins slave кончится место или память и сборка упадет. И это все - только примеры из моего опыта. А суть в следующем: инструменты DevOps имеют свои ограничения по объему данных и нагрузке, и нужно либо допиливать их напильником, либо не делать таких больших монолитов. Второй путь проще)
5) если микросервис не оправдал ожиданий - его можно выкинуть и переписать. Я не говорю, что это типовая практика, я говорю, что такая опция есть. Решение принимается на уровне команды\отдела\кластера, а не вице-президента по ИТ в случае монолита)
Резюмирую - если кода меньше, его проще выводить в ПРОМ, поддерживать и если надо выкинуть.
О минусах - в следующем посте.
#microservices #сравнение
Поговорим про микросервисы. Статей будет несколько, от плюсов и минусов до саги и прочих паттернов.
Для начала что такое микросервис. Я дам определение в виде набора обязательных признаков. Обязательны все из них.
1) одна команда
2) отдельная кодовая база
3) отдельная БД
4) отдельный pipeline
5) отдельный релизный цикл
6) небольшой объем кода
7) взаимодействие с другими сервисами через межпроцессное API. REST, gRPC, GraphQL, Kafka - это то, что сейчас на слуху, на самом деле технологий больше.
Для начала рассмотрим плюсы:
1) один сервис разрабатывает одна команда. При этом одна команда может делать несколько приложений, но главное здесь - у одного приложения один хозяин, определяющий вектор его развития. Такие ключевые решения, как: язык, на котором написан код, используемые библиотеки, утилита для сборки и архитектура в конце концов) Проще договорится, меньше согласований и зависимостей от внешних людей или команд. Этого же результата помогает достичь отдельный релизный цикл. Хотя в случае enterprise микросервисов зависимостей не избежать и это боль( Еще одно замечание - микросервис может релизиться независимо при условии использования фича-тогглов https://martinfowler.com/articles/feature-toggles.html и поддержки совместимости API. Альтернатива фича тогглов - писать код так, чтобы его неготовый кусок невозможно было вызывать снаружи до момента полной готовности вас и смежников.
2) МИКРОсервис означает, что кода не должно быть много. Сколько тысяч строк - явно не миллион и не сто тысяч, скорее несколько тысяч. А раз кода не много - в него проще погрузится. Меньше кривая обучения для новых членов команды. А если ты в целом понимаешь как работает приложение - с большей вероятностью доработки и рефакторинг будут сохранять его исходную архитектуру или менять ее единообразно. Как результат сервис будет оставаться понятным и далее.
3) проект быстрее компилируется и запускается, быстрее прогоняются тесты - быстрее обратная связь, быстрее процесс разработки. Проще делать маленькие Pull Request, ревьюверы меньше матерятся, т.к. не видят 100+ файлов в Pull Request, проще слияния. Вообще писать код нужно так, чтобы merge конфликтов не было, но это идеал, к которому надо стремится)
4) DevOps инструменты и практики будут работать "из коробки". На проектах с сотнями тысяч строк pipeline будет падать. И падать достаточно часто. SonarQube не может определить дельту Pull Request и падает. Checkmarx нужно дорабатывать у вендора, чтобы не падал. Джобы сборки оптимизировать, параллелить, чтобы добиться приемлемого времени сборки. Интеграционные тесты падают, т.к. контекст, необходимый для поднятия приложения, постоянно растет. База становится такой сложной, что DBA наотрез отказываются от Liquibase и накатывают скрипты руками пошагово. Число интеграций такое, что набор примочных тестов запущенный автоматически с большой вероятностью упадет и встанет вопрос - а точно нам нужны автотесты? НТшники работают по ночам. Bitbucket дико тормозит, когда размер репозитория превышает 2 Тб, а это вполне реально - ведь хранится вся история изменений. Да и в принципе по статистике чем дольше сборка и deploy, тем больше вероятность, что на jenkins slave кончится место или память и сборка упадет. И это все - только примеры из моего опыта. А суть в следующем: инструменты DevOps имеют свои ограничения по объему данных и нагрузке, и нужно либо допиливать их напильником, либо не делать таких больших монолитов. Второй путь проще)
5) если микросервис не оправдал ожиданий - его можно выкинуть и переписать. Я не говорю, что это типовая практика, я говорю, что такая опция есть. Решение принимается на уровне команды\отдела\кластера, а не вице-президента по ИТ в случае монолита)
Резюмирую - если кода меньше, его проще выводить в ПРОМ, поддерживать и если надо выкинуть.
О минусах - в следующем посте.
#microservices #сравнение
martinfowler.com
Feature Toggles (aka Feature Flags)
Feature Flags can be categorized into several buckets; manage each appropriately. Smart implementation can help constrain complexity.
Вдогонку - забыл про два важных преимущества микросервисов не с точки зрения команды, а для компании в целом:
6) микросервис можно независимо масштабировать. Т.е. давать больше ресурсов для более нагруженных частей системы. Особенно легко это реализуется в случае stateless сервисов в облаке - через ресурс типа HorizontalPodAutoscaler. Для сервисов с состоянием тоже возможны варианты - sticky session если речь про кэширование состояния или шардирование для хранилищ данных. Фактор масштабирования стоит учитывать при разделении функционала на микросервисы.
7) если в компании есть команды, разрабатывающие на разных языках или можно купить\внедрить готовое решение - микросервисная архитектура легко позволяет это сделать, т.к. интеграция между сервисами идет через межпроцессное API
#microserices #сравнение
6) микросервис можно независимо масштабировать. Т.е. давать больше ресурсов для более нагруженных частей системы. Особенно легко это реализуется в случае stateless сервисов в облаке - через ресурс типа HorizontalPodAutoscaler. Для сервисов с состоянием тоже возможны варианты - sticky session если речь про кэширование состояния или шардирование для хранилищ данных. Фактор масштабирования стоит учитывать при разделении функционала на микросервисы.
7) если в компании есть команды, разрабатывающие на разных языках или можно купить\внедрить готовое решение - микросервисная архитектура легко позволяет это сделать, т.к. интеграция между сервисами идет через межпроцессное API
#microserices #сравнение
Всем привет!
Пару заблуждений про микросервисы.
1) Облачные технологии (k8s) не равно микросервисам. Да, если вы начинаете проект с микросервисами с нуля и есть возможность развернуть его в облаке - так и нужно делать.
Облако упрощает развертывание и масштабирование микросервисов, добавляет отказоустойчивость. Но микросервисы - это архитектура. А k8s - технологии.
Аналогия - REST обычно используют с JSON, для того же Spring RestController это настройка по умолчанию. Но никто не мешает в REST API возвращать XML.
Как я писал ранее: по большому счету микросервис - это про сознательное ограничение объема кода\функционала и связанные с этим плюшки.
2) трушный микросервис не обязан реализовывать строго одну операцию. Как именно выделять микросервисы - отдельная тема. Базовые варианты: по функционалу, по архитектурному слою - например, слой хранения - или по требованиям к масштабированию.
Но в любом из этих случаев в API, которое выставляет сервис, может быть несколько логически связанных методов. Ключевое слове здесь - логически связанных.
Если я что-то упустил, есть вопросы или возражения - пишите.
#microservices
Пару заблуждений про микросервисы.
1) Облачные технологии (k8s) не равно микросервисам. Да, если вы начинаете проект с микросервисами с нуля и есть возможность развернуть его в облаке - так и нужно делать.
Облако упрощает развертывание и масштабирование микросервисов, добавляет отказоустойчивость. Но микросервисы - это архитектура. А k8s - технологии.
Аналогия - REST обычно используют с JSON, для того же Spring RestController это настройка по умолчанию. Но никто не мешает в REST API возвращать XML.
Как я писал ранее: по большому счету микросервис - это про сознательное ограничение объема кода\функционала и связанные с этим плюшки.
2) трушный микросервис не обязан реализовывать строго одну операцию. Как именно выделять микросервисы - отдельная тема. Базовые варианты: по функционалу, по архитектурному слою - например, слой хранения - или по требованиям к масштабированию.
Но в любом из этих случаев в API, которое выставляет сервис, может быть несколько логически связанных методов. Ключевое слове здесь - логически связанных.
Если я что-то упустил, есть вопросы или возражения - пишите.
#microservices
Всем привет!
И снова микросервисы.
Из предыдущих постов видно, что разработчикам и команде в целом проще работать с микросервисами. Компании, которой принадлежит продукт, тоже, т.к. снижаются риски. Неужели так все радужно? Не совсем)
И основные трудности ложатся на плечи сопровождения и третьей линии поддержки. Причина проста - увеличивается число единиц деплоймента, увеличивается число интеграций и, следовательно, зависимостей между сервисами. Сложнее разворачивать, т.к. надо учитывать зависимости, сложнее разбирать ошибки, т.к. недостаточно посмотреть логи одного приложения как это было с монолитом. Также увеличивается риск каскадных сбоев, когда из-за сбоя одного ключевого сервиса "подвисают" его входящие сетевые соединения, переполняются пулы потоков и все падает большое количество серверов.
Что тут можно сделать?
1) с увеличением числа единиц деплоймента сделать ничего нельзя, но упросить сам процесс деплоя позволяет k8s
2) проблему сложных зависимостей между микросервисами надо решать через поддержку совместимости и версионирование API и фича тогглы. Т.е. поставщик данных при любых изменениях версионирует API какое-то время сохраняя старую версию, а потребитель включает новую фичу для клиентов по рубильнику по готовности поставщика. Это позволит избежать синхронных внедрений нескольких сервисов. Синхронные внедрения - "зло", я об этом уже писал: https://t.me/javaKotlinDevOps/24 Решение звучит просто, а реализуется исходя из моего опыта сложно) Как его добиться? Только дисциплиной, серебряной пули нет. Ну и учитывая версионирование API при проектировании сервиса, а также достаточным набором модульных и приемочных тестов чтобы не поломать API. Как определить достаточность? Очень просто: если есть чувство уверенности, что раз тесты прошли, то API не сломали - значит набор достаточен))) Для того, чтобы потребитель и поставщик одинаково представляли себе новую версию API она обязательно должна храниться в системе контроля версий (VCS). А убедиться в совместимости позволяет технология Consumer Driven Contract (CDC) https://martinfowler.com/articles/consumerDrivenContracts.html Самые известные фреймворки ее реализующие - Spring Cloud Contract и Pact.
3) проблему разбора багов и инцидентов помогают решить централизованный сбор логов и метрик, а главное - распределенная трассировка. Это технология для отслеживания всей цепочки запросов, включая список сервисов, через который прошел запрос, тайминги и информацию о возникающих ошибках. Для трассировки есть стандарт - OpenTelemetry https://opentelemetry.io/docs/concepts/what-is-opentelemetry/. Вот неплохое видео, описывающее архитектуру трассировки в целом и сравнивающее две его самые известные реализации - Zipkin и Jaeger. https://youtu.be/6PiThk3QHWw?t=4191 См. вторую часть видео, ссылка указывает на нее. А вот статья, описывающая проблемы текущих решений - https://habr.com/ru/company/flant/blog/460365/
4) решить проблему каскадных сбоев позволяют ограниченные и согласованные таймауты и реализация circuit breakers https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker Согласованность таймаутов можно обеспечить в ручном режиме, а можно передачей значений таймаута в API от клиента к серверу. Надо сказать, что данные паттерны важны и для монолита, но для микросервисов из-за увеличения числа интеграций их сложнее контролировать. Паттерны можно реализовать на клиенте, а можно с помощью Service Mesh, например, Istio - https://istio.io/latest/docs/concepts/traffic-management/#network-resilience-and-testing Последний вариант дает возможность централизованного контроля за настройкой таймаутов и circuit breaker.
#microservices #сравнение
И снова микросервисы.
Из предыдущих постов видно, что разработчикам и команде в целом проще работать с микросервисами. Компании, которой принадлежит продукт, тоже, т.к. снижаются риски. Неужели так все радужно? Не совсем)
И основные трудности ложатся на плечи сопровождения и третьей линии поддержки. Причина проста - увеличивается число единиц деплоймента, увеличивается число интеграций и, следовательно, зависимостей между сервисами. Сложнее разворачивать, т.к. надо учитывать зависимости, сложнее разбирать ошибки, т.к. недостаточно посмотреть логи одного приложения как это было с монолитом. Также увеличивается риск каскадных сбоев, когда из-за сбоя одного ключевого сервиса "подвисают" его входящие сетевые соединения, переполняются пулы потоков и все падает большое количество серверов.
Что тут можно сделать?
1) с увеличением числа единиц деплоймента сделать ничего нельзя, но упросить сам процесс деплоя позволяет k8s
2) проблему сложных зависимостей между микросервисами надо решать через поддержку совместимости и версионирование API и фича тогглы. Т.е. поставщик данных при любых изменениях версионирует API какое-то время сохраняя старую версию, а потребитель включает новую фичу для клиентов по рубильнику по готовности поставщика. Это позволит избежать синхронных внедрений нескольких сервисов. Синхронные внедрения - "зло", я об этом уже писал: https://t.me/javaKotlinDevOps/24 Решение звучит просто, а реализуется исходя из моего опыта сложно) Как его добиться? Только дисциплиной, серебряной пули нет. Ну и учитывая версионирование API при проектировании сервиса, а также достаточным набором модульных и приемочных тестов чтобы не поломать API. Как определить достаточность? Очень просто: если есть чувство уверенности, что раз тесты прошли, то API не сломали - значит набор достаточен))) Для того, чтобы потребитель и поставщик одинаково представляли себе новую версию API она обязательно должна храниться в системе контроля версий (VCS). А убедиться в совместимости позволяет технология Consumer Driven Contract (CDC) https://martinfowler.com/articles/consumerDrivenContracts.html Самые известные фреймворки ее реализующие - Spring Cloud Contract и Pact.
3) проблему разбора багов и инцидентов помогают решить централизованный сбор логов и метрик, а главное - распределенная трассировка. Это технология для отслеживания всей цепочки запросов, включая список сервисов, через который прошел запрос, тайминги и информацию о возникающих ошибках. Для трассировки есть стандарт - OpenTelemetry https://opentelemetry.io/docs/concepts/what-is-opentelemetry/. Вот неплохое видео, описывающее архитектуру трассировки в целом и сравнивающее две его самые известные реализации - Zipkin и Jaeger. https://youtu.be/6PiThk3QHWw?t=4191 См. вторую часть видео, ссылка указывает на нее. А вот статья, описывающая проблемы текущих решений - https://habr.com/ru/company/flant/blog/460365/
4) решить проблему каскадных сбоев позволяют ограниченные и согласованные таймауты и реализация circuit breakers https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker Согласованность таймаутов можно обеспечить в ручном режиме, а можно передачей значений таймаута в API от клиента к серверу. Надо сказать, что данные паттерны важны и для монолита, но для микросервисов из-за увеличения числа интеграций их сложнее контролировать. Паттерны можно реализовать на клиенте, а можно с помощью Service Mesh, например, Istio - https://istio.io/latest/docs/concepts/traffic-management/#network-resilience-and-testing Последний вариант дает возможность централизованного контроля за настройкой таймаутов и circuit breaker.
#microservices #сравнение
Telegram
(java || kotlin) && devOps
Всем привет!
Современная разработка - это микросервисы, микросервисы - это много внешних взаимодействий, а внешние взаимодействия - это API.
Каким должно быть хорошее API?
1) API должно быть версионировано. Все течет, все меняется: бизнес-требования, новые…
Современная разработка - это микросервисы, микросервисы - это много внешних взаимодействий, а внешние взаимодействия - это API.
Каким должно быть хорошее API?
1) API должно быть версионировано. Все течет, все меняется: бизнес-требования, новые…
Всем привет!
Всегда ли нужно распиливать систему на микросервисы? Не всегда.
Я вижу как минимум один кейс.
Если выполняется хотя бы одно условие:
1) у вас одна команда
2) сроки ограничены
3) деньги ограничены
3) речь про стартап
4) создается приложение для проверки гипотезы
- стоит рассмотреть вариант с монолитом.
Плюсы - потребуется меньше железа, проще настраивать пайплайн и инфраструктуру. В простейшем случае можно весь ПРОМ развернуть на одном сервере. В случае стартапа или проверки гипотезы когда ещё не ясно как правильно разделить систему на независимые контексты ещё не понятно - в случае монолита ее проще рефакторить. Пока он маленький)
Может возникнуть вопрос - а как дальше масштабировать? Ответ - делите систему на уровне модулей Maven\Gradle, контролируйте зависимости между уровнями приложения, избегайте циклических ссылок. Для всего этого есть утилиты, напишу о них далее. Ну и конечно нужен набор приемочных тестов, его можно реализовать в период разделения на микросервисы, запуская вначале на "монолите", потом на микросервисах. С точки зрения БД - храните данные в разных схемах или хотя бы таблицах. Первый вариант лучше и одновременно хуже тем, что между схемами невозможны транзакции. Проектирование с использованием DDD - Domain driven development - в частности концепция агрегата, тоже хороший инструмент для создания разделяемого монолита. Тогда из этих модулей можно будет в будущем сделать микросервисы. Да, в любом случае при разделении потребуется дополнительная работа, но зато значительно увеличивается время вывода в ПРОМ и получение обратной связи. А дополнительную работу предлагаю рассматривать как рефакторинг.
Какие риски у этого подхода - по большому счету один. Если маленький монолит, который задумывался временным решением, превратится в большой и станет постоянным. Тогда в какой-то момент технологии устареют, части монолита срастутся сильнее и возникнет вопрос - а стоит ли его вообще разделять? Или проще переписать заново)))
И главное, что стоит запомнить - у каждого микросервиса должна быть веская причина существования. Если есть сомнения - нужен ли еще микросервис, не надо его выделять. Микросервис - далека не бесплатная штука в плане разработки и поддержки
#microservices #сравнение
Всегда ли нужно распиливать систему на микросервисы? Не всегда.
Я вижу как минимум один кейс.
Если выполняется хотя бы одно условие:
1) у вас одна команда
2) сроки ограничены
3) деньги ограничены
3) речь про стартап
4) создается приложение для проверки гипотезы
- стоит рассмотреть вариант с монолитом.
Плюсы - потребуется меньше железа, проще настраивать пайплайн и инфраструктуру. В простейшем случае можно весь ПРОМ развернуть на одном сервере. В случае стартапа или проверки гипотезы когда ещё не ясно как правильно разделить систему на независимые контексты ещё не понятно - в случае монолита ее проще рефакторить. Пока он маленький)
Может возникнуть вопрос - а как дальше масштабировать? Ответ - делите систему на уровне модулей Maven\Gradle, контролируйте зависимости между уровнями приложения, избегайте циклических ссылок. Для всего этого есть утилиты, напишу о них далее. Ну и конечно нужен набор приемочных тестов, его можно реализовать в период разделения на микросервисы, запуская вначале на "монолите", потом на микросервисах. С точки зрения БД - храните данные в разных схемах или хотя бы таблицах. Первый вариант лучше и одновременно хуже тем, что между схемами невозможны транзакции. Проектирование с использованием DDD - Domain driven development - в частности концепция агрегата, тоже хороший инструмент для создания разделяемого монолита. Тогда из этих модулей можно будет в будущем сделать микросервисы. Да, в любом случае при разделении потребуется дополнительная работа, но зато значительно увеличивается время вывода в ПРОМ и получение обратной связи. А дополнительную работу предлагаю рассматривать как рефакторинг.
Какие риски у этого подхода - по большому счету один. Если маленький монолит, который задумывался временным решением, превратится в большой и станет постоянным. Тогда в какой-то момент технологии устареют, части монолита срастутся сильнее и возникнет вопрос - а стоит ли его вообще разделять? Или проще переписать заново)))
И главное, что стоит запомнить - у каждого микросервиса должна быть веская причина существования. Если есть сомнения - нужен ли еще микросервис, не надо его выделять. Микросервис - далека не бесплатная штука в плане разработки и поддержки
#microservices #сравнение
Всем привет!
Поговорим про разделение функционала между микросервисами.
Для начала я бы разделил два случая - распил монолита или создание нового приложения.
При распиле есть несколько путей, которые можно комбинировать:
1) отделять в первую очередь менее значительный функционал. Так можно будет набить шишки, настроить DevOps, инфраструктуру, а потом уже переходить к чему-то серьёзному
2) отделять функционал, который легче выделить. Например, он уже компилируется в отдельный war/ear, хотя и имеет общий релизный цикл с монолитом. Или он правильно спроектирован https://t.me/javaKotlinDevOps/59 и его отделение требует минимальных усилий.
3) делать новые фичи на микросервисах, закрывая монолит API Gateway для единообразия API.
Все это называется шаблоном Душитель https://microservices.io/patterns/refactoring/strangler-application.html Душим мы как вы понимаете монолит)
Если же мы делаем новый функционал - тоже есть разные варианты, размещу их в порядке приоритета
1) разделение по бизнес фичам. Где фича - это агрегат в терминах DDD. Или ресурс в REST. Как я уже говорил ранее - не операция, это слишком мелко https://t.me/javaKotlinDevOps/59
2) разделение по слоям. Например, можно выделить в отдельный сервис сохранение данных. Или шлюз к смежной системе
3) разделение по планируемой нагрузке. Я бы его применял как дополнительный фактор к первым двум. Более нагруженные сервисы выделяем отдельно.
Еще один важный фактор: возможность переиспользования сервиса. Имеет значение и при распиле монолита, и при разработке новых фичей. Пример: отправка уведомлений пользователю по-хорошему стандартизирована и такой сервис смогут переиспользовать все создаваемые микросервисы. Способствуем выполнению принципа DRY - https://ru.wikipedia.org/wiki/Don’t_repeat_yourself
И напоследок хотел бы напомнить простую истину - как нет серебряной пули или единственно верной архитектуры, так нет канонического способа разделения на микросервисы)
#microservices #сравнение
Поговорим про разделение функционала между микросервисами.
Для начала я бы разделил два случая - распил монолита или создание нового приложения.
При распиле есть несколько путей, которые можно комбинировать:
1) отделять в первую очередь менее значительный функционал. Так можно будет набить шишки, настроить DevOps, инфраструктуру, а потом уже переходить к чему-то серьёзному
2) отделять функционал, который легче выделить. Например, он уже компилируется в отдельный war/ear, хотя и имеет общий релизный цикл с монолитом. Или он правильно спроектирован https://t.me/javaKotlinDevOps/59 и его отделение требует минимальных усилий.
3) делать новые фичи на микросервисах, закрывая монолит API Gateway для единообразия API.
Все это называется шаблоном Душитель https://microservices.io/patterns/refactoring/strangler-application.html Душим мы как вы понимаете монолит)
Если же мы делаем новый функционал - тоже есть разные варианты, размещу их в порядке приоритета
1) разделение по бизнес фичам. Где фича - это агрегат в терминах DDD. Или ресурс в REST. Как я уже говорил ранее - не операция, это слишком мелко https://t.me/javaKotlinDevOps/59
2) разделение по слоям. Например, можно выделить в отдельный сервис сохранение данных. Или шлюз к смежной системе
3) разделение по планируемой нагрузке. Я бы его применял как дополнительный фактор к первым двум. Более нагруженные сервисы выделяем отдельно.
Еще один важный фактор: возможность переиспользования сервиса. Имеет значение и при распиле монолита, и при разработке новых фичей. Пример: отправка уведомлений пользователю по-хорошему стандартизирована и такой сервис смогут переиспользовать все создаваемые микросервисы. Способствуем выполнению принципа DRY - https://ru.wikipedia.org/wiki/Don’t_repeat_yourself
И напоследок хотел бы напомнить простую истину - как нет серебряной пули или единственно верной архитектуры, так нет канонического способа разделения на микросервисы)
#microservices #сравнение
Telegram
(java || kotlin) && devOps
Всем привет!
Всегда ли нужно распиливать систему на микросервисы? Не всегда.
Я вижу как минимум один кейс.
Если выполняется хотя бы одно условие:
1) у вас одна команда
2) сроки ограничены
3) деньги ограничены
3) речь про стартап
4) создается приложение…
Всегда ли нужно распиливать систему на микросервисы? Не всегда.
Я вижу как минимум один кейс.
Если выполняется хотя бы одно условие:
1) у вас одна команда
2) сроки ограничены
3) деньги ограничены
3) речь про стартап
4) создается приложение…
Всем привет!
Хочу разбавить тему микросервисов. Сегодня поговорим про Spring и Dependency Injection (DI).
Есть три способа внедрения бина:
1) через конструктор
2) через поле
3) через setter
Практически все слышали, что предпочтительнее внедрять через конструктор. Вопрос почему?
Первая и самая главная причина: внедрение зависимостей - это часть инициализации объекта. А конструктор - это простой и естественный способ инициализировать объект.
Что из этого следует:
1) список внедряемых бинов легко читается. Через setter-ы очень плохо читается, через поля получше, но конструктор вне конкуренции.
2) становится проще создать полностью инициализированный объект т.к. для этого есть готовый конструктор. Полезно в тестах, можно написать тест на чистом JUnit, без @SpringBootTest и других инструментов для инициализации контекста из Spring Test.
3) если классу нужно слишком много бинов для работы - SonarQube будет ругаться, что у конструктора слишком много полей. Считаю это плюсом, если ругается - это повод задуматься над рефакторингом
4) если у бинов циклические ссылки - инициализация контекста Spring упадет. Многие считают это минусом, но я не согласен: чем раньше найдешь циклическую зависимость - тем быстрее исправишь. См. https://t.me/javaKotlinDevOps/53
5) если с внедряемыми бинами надо что-то делать, то у нас есть место, где гарантированно все бины будут доступны - конструктор. При внедрении зависимостей через setter или поля вначале выполнится дефолтный конструктор, потом будут проиницилизированы поля.
6) для инициализации не используется рефлексия, как в случае autowired полей, т.е. инициализации работает немного быстрее
7) если конструктор единственный, то начиная со Spring 4.3 не нужно указывать аннотацию @Autowired. Для Kotlin в случае default конструктора есть особенности, для того, чтобы с ними не сталкиваться лучше использовать Spring 5+ ) Не сказать, что это большое преимущество, т.к. аннотацию для маркировки бина все равно придется использовать, поэтому класс без import Spring зависимостей так просто сделать не получится.
И второй важный момент - поддержка иммутабельности объектов, т.к. только в конструкторе можно инициализировать final поля в случае Java и val - в случае Kotlin.
#spring #java #kotlin #interview_question
Хочу разбавить тему микросервисов. Сегодня поговорим про Spring и Dependency Injection (DI).
Есть три способа внедрения бина:
1) через конструктор
2) через поле
3) через setter
Практически все слышали, что предпочтительнее внедрять через конструктор. Вопрос почему?
Первая и самая главная причина: внедрение зависимостей - это часть инициализации объекта. А конструктор - это простой и естественный способ инициализировать объект.
Что из этого следует:
1) список внедряемых бинов легко читается. Через setter-ы очень плохо читается, через поля получше, но конструктор вне конкуренции.
2) становится проще создать полностью инициализированный объект т.к. для этого есть готовый конструктор. Полезно в тестах, можно написать тест на чистом JUnit, без @SpringBootTest и других инструментов для инициализации контекста из Spring Test.
3) если классу нужно слишком много бинов для работы - SonarQube будет ругаться, что у конструктора слишком много полей. Считаю это плюсом, если ругается - это повод задуматься над рефакторингом
4) если у бинов циклические ссылки - инициализация контекста Spring упадет. Многие считают это минусом, но я не согласен: чем раньше найдешь циклическую зависимость - тем быстрее исправишь. См. https://t.me/javaKotlinDevOps/53
5) если с внедряемыми бинами надо что-то делать, то у нас есть место, где гарантированно все бины будут доступны - конструктор. При внедрении зависимостей через setter или поля вначале выполнится дефолтный конструктор, потом будут проиницилизированы поля.
6) для инициализации не используется рефлексия, как в случае autowired полей, т.е. инициализации работает немного быстрее
7) если конструктор единственный, то начиная со Spring 4.3 не нужно указывать аннотацию @Autowired. Для Kotlin в случае default конструктора есть особенности, для того, чтобы с ними не сталкиваться лучше использовать Spring 5+ ) Не сказать, что это большое преимущество, т.к. аннотацию для маркировки бина все равно придется использовать, поэтому класс без import Spring зависимостей так просто сделать не получится.
И второй важный момент - поддержка иммутабельности объектов, т.к. только в конструкторе можно инициализировать final поля в случае Java и val - в случае Kotlin.
#spring #java #kotlin #interview_question
Telegram
(java || kotlin) && devOps
Всем привет!
Сегодня рассмотрим циклические зависимости. Начнем с уровня классов и дойдем до слоев приложения.
1) классы. Технически Java компилятор умеет компилировать взаимозависимые классы за счет того, что является многопроходным - https://ru.wikip…
Сегодня рассмотрим циклические зависимости. Начнем с уровня классов и дойдем до слоев приложения.
1) классы. Технически Java компилятор умеет компилировать взаимозависимые классы за счет того, что является многопроходным - https://ru.wikip…
Всем привет!
Продолжается перерыв в теме микросервисов.
Сегодня будет немного холивара)
Можно ли использовать static методы?
Исторически static-и прошли 3 стадии в своем восприятии.
1 этап: о круто, можно не создавать объект, давайте все методы делать статическими.
Если что - я конечно утрирую насчет всех, но static-и активно использовались, это факт, который легко увидеть в legacy коде. Еще факт: static-и есть во многих языках, что как бы намекает. Даже в Kotlin они есть, просто выглядят странно - я про companion object.
2 этап: static-и трудно тестировать, это антипаттерн, не надо их использовать. Ну разве что для Util классов, и то ...
Вот пример, демонстрирующий почему возникают такие мысли.
Возьмем другой антипаттерн - синглетон.
class Singleton {
private static final instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
....
}
Сразу скажу - это не одна из простейших реализаций, не потокобезопасная, взятая для примера, так делать не надо.
Предположим, мы хотим сделать вместо Singleton заглушку для теста. Ну например, он ходит в БД или читает данные из файла, а мы хотим, чтобы тесты работали быстро.
Можно сделать так:
class TestSingleton extends Singleton {
private static final testInstance = new TestSingleton();
public static Singleton getTestInstance() {
return testInstance;
}
....
}
Но это работает только если Sigleton можно как-то передать в тестируемый класс, он же System under Test (SUT).
А если это зависимость зависимости SUT? Или в более общем случае его нельзя передать через public API?
Основная проблема в том, что static методы не наследуются, а следовательно переопределить их нельзя. Можно создать метод с тем же именем, но вызываться будет метод того класса, который указан в конкретном куске кода. Или декларируемого, а не фактического класса переменной, поля или параметра, если static вызывается на объекте. В этом плане Java позволяя вызывать static метод из объекта только путает людей( К слову, в Kotlin так нельзя.
Есть конечно грязные хаки с мокированием static методов, даже Mockito это умеет. Но тот факт, что для включения этой фичи нужно добавить настройку в classpath https://www.baeldung.com/mockito-mock-static-methods говорит о том, что авторы Mockito не рекомендуют так делать.
3 этап, наше время: все не так однозначно. Точнее однозначно вот что: если static метод используется в паре со static полями - это точно зло.
И самостоятельное создание синглетонов тоже зло) Это я на всякий случай уточняю)
Но если присмотрется к вот такому static методу:
1) он не имеет доступа к полям объекта по определению
2) в классе нет static полей как я писал выше
3) пусть метод не имеет побочных эффектов. Т.е. не лезет в БД, в файловую систему, в другие сервисы
Т.е метод получает что-то на вход, вычисляет что-то и возвращает результат.
Это типичный метод Util класса, да.
Но еще это определение функции из математики. А функция - это функциональное программирование. А Java начиная с 8 версии умеет передавать ссылки на функции. И хотя в ней нет функциональных типов, но есть функциональные интерфейсы, которые делают тоже самое, просто немного с большим количеством кода. Java же, все как мы любим)
Подводя итог - считать static злом не надо. Надо лишь правильно его использовать.
#interview_question #holy_war #java #kotlin
Продолжается перерыв в теме микросервисов.
Сегодня будет немного холивара)
Можно ли использовать static методы?
Исторически static-и прошли 3 стадии в своем восприятии.
1 этап: о круто, можно не создавать объект, давайте все методы делать статическими.
Если что - я конечно утрирую насчет всех, но static-и активно использовались, это факт, который легко увидеть в legacy коде. Еще факт: static-и есть во многих языках, что как бы намекает. Даже в Kotlin они есть, просто выглядят странно - я про companion object.
2 этап: static-и трудно тестировать, это антипаттерн, не надо их использовать. Ну разве что для Util классов, и то ...
Вот пример, демонстрирующий почему возникают такие мысли.
Возьмем другой антипаттерн - синглетон.
class Singleton {
private static final instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
....
}
Сразу скажу - это не одна из простейших реализаций, не потокобезопасная, взятая для примера, так делать не надо.
Предположим, мы хотим сделать вместо Singleton заглушку для теста. Ну например, он ходит в БД или читает данные из файла, а мы хотим, чтобы тесты работали быстро.
Можно сделать так:
class TestSingleton extends Singleton {
private static final testInstance = new TestSingleton();
public static Singleton getTestInstance() {
return testInstance;
}
....
}
Но это работает только если Sigleton можно как-то передать в тестируемый класс, он же System under Test (SUT).
А если это зависимость зависимости SUT? Или в более общем случае его нельзя передать через public API?
Основная проблема в том, что static методы не наследуются, а следовательно переопределить их нельзя. Можно создать метод с тем же именем, но вызываться будет метод того класса, который указан в конкретном куске кода. Или декларируемого, а не фактического класса переменной, поля или параметра, если static вызывается на объекте. В этом плане Java позволяя вызывать static метод из объекта только путает людей( К слову, в Kotlin так нельзя.
Есть конечно грязные хаки с мокированием static методов, даже Mockito это умеет. Но тот факт, что для включения этой фичи нужно добавить настройку в classpath https://www.baeldung.com/mockito-mock-static-methods говорит о том, что авторы Mockito не рекомендуют так делать.
3 этап, наше время: все не так однозначно. Точнее однозначно вот что: если static метод используется в паре со static полями - это точно зло.
И самостоятельное создание синглетонов тоже зло) Это я на всякий случай уточняю)
Но если присмотрется к вот такому static методу:
1) он не имеет доступа к полям объекта по определению
2) в классе нет static полей как я писал выше
3) пусть метод не имеет побочных эффектов. Т.е. не лезет в БД, в файловую систему, в другие сервисы
Т.е метод получает что-то на вход, вычисляет что-то и возвращает результат.
Это типичный метод Util класса, да.
Но еще это определение функции из математики. А функция - это функциональное программирование. А Java начиная с 8 версии умеет передавать ссылки на функции. И хотя в ней нет функциональных типов, но есть функциональные интерфейсы, которые делают тоже самое, просто немного с большим количеством кода. Java же, все как мы любим)
Подводя итог - считать static злом не надо. Надо лишь правильно его использовать.
#interview_question #holy_war #java #kotlin
Baeldung
Mocking Static Methods With Mockito | Baeldung
Explore a couple of examples of how we can use Mockito to mock static methods.
Всем привет!
И еще один оффтопик)
Наверняка многие знают, что IDEA облегчает рефакторинг кода. Когда-то, лет 10 назад, для меня это было одним из основных ее преимуществом.
Со временем там много чего интересного появилось, см. https://t.me/javaKotlinDevOps/28
К чему это я.
Рефакторинги в IDEA являются контекстно-зависимыми.
Т.е. список доступных зависит:
1) от языка
2) от места, в котором вы кликнули мышью или вызвали shortcut.
Кстати, для рефакторингов есть один master shortcut, который точно стоит запомнить: Ctrl+Alt+Shift+T.
Он отображает все доступные в данном месте рефакторинги.
Так вот, я подозреваю, что многие знают к примеру Extract Method или Introduce Variable, но почти никто - Remove Middleman.
Поэтому рекомендую ознакомится со списком возможных рефакторингов.
На русском и на хабре:
https://habr.com/ru/post/530360/
В официальной документации:
https://www.jetbrains.com/help/idea/refactoring-source-code.html
И видос от создателей, как устроены сложные рефакторинги и как их выстраивать в цепочки, не дожидаясь, когда допилят IDEA)
https://www.youtube.com/watch?v=tmv_Grdbog4
#idea #IDE
И еще один оффтопик)
Наверняка многие знают, что IDEA облегчает рефакторинг кода. Когда-то, лет 10 назад, для меня это было одним из основных ее преимуществом.
Со временем там много чего интересного появилось, см. https://t.me/javaKotlinDevOps/28
К чему это я.
Рефакторинги в IDEA являются контекстно-зависимыми.
Т.е. список доступных зависит:
1) от языка
2) от места, в котором вы кликнули мышью или вызвали shortcut.
Кстати, для рефакторингов есть один master shortcut, который точно стоит запомнить: Ctrl+Alt+Shift+T.
Он отображает все доступные в данном месте рефакторинги.
Так вот, я подозреваю, что многие знают к примеру Extract Method или Introduce Variable, но почти никто - Remove Middleman.
Поэтому рекомендую ознакомится со списком возможных рефакторингов.
На русском и на хабре:
https://habr.com/ru/post/530360/
В официальной документации:
https://www.jetbrains.com/help/idea/refactoring-source-code.html
И видос от создателей, как устроены сложные рефакторинги и как их выстраивать в цепочки, не дожидаясь, когда допилят IDEA)
https://www.youtube.com/watch?v=tmv_Grdbog4
#idea #IDE
Telegram
(java || kotlin) && devOps
Всем привет!
Помимо знания языка программирования и инструментов, о которых я писал в предыдущих постах, есть еще один важный навык, который часто недооценивают. Я про знание среды разработки, она же IDE. Это инструмент, с которым мы работаем каждый день.…
Помимо знания языка программирования и инструментов, о которых я писал в предыдущих постах, есть еще один важный навык, который часто недооценивают. Я про знание среды разработки, она же IDE. Это инструмент, с которым мы работаем каждый день.…
Всем привет!
На какие грабли можно наступить при распиле монолита на микросервисы?
Небольшой дисклеймер: я подразумеваю, что внедряются микросервисы + Agile, т.к по определению у микросервиса должен быть владелец - команда.
Примеры взяты из жизни)
1) Как я уже говорил https://t.me/javaKotlinDevOps/60 - начинать распил лучше с каких-то небольших частей существующего монолита или новых фичей для этого монолита. Тут важен вот какой момент - скорее всего монолит выполняет некую критическую функцию для компании и, следовательно, для него существуют некие требования. Например, 99,99-ая доступность. И первые "тренировочные" микросервисы не стоит делать для другого сервиса=приложения=монолита, например, потому, что у него более лояльные пользователи или там проще внедрение. Если так сделать, то при переходе от простого монолита к сложному появится куча неожиданных требований по надежности, масштабируемости, ИБ, удобству сопровождения, и архитектуру придется переделывать.
2) должна быть зафиксированная и согласованная со всеми заинтересованными лицами архитектура.
Почему это критично - потому что архитектура - это основа. Можно конечно создавать архитектуру параллельно разработке, по принципу ввяжемся в бой, а там разберемся. Но это приведет к переписыванию микросервисов, к микросервисам-фантомам, которые на бумаге есть, а в жизни оказались не нужны, и к их антиподам, про которых просто забыли и конечно же к проблемам на всех стендах из-за меняющегося API. В конце концов при таком подходе слова архитектура и архитектор по отношению к новой микросервисной платформе станут ругательными)
3) нужно время на обучение разработчиков, тестировщиков и сопровождения. Обучение может быть и должно быть на практике - на реальных сервисах, но оно должно быть учтено в roadmap перехода. Сроки перехода должны быть согласованы командами разработки и сопровождения, а не спущены сверху. Звучит очевидно, но по моему опыту не для всех)
4) нужно изменение релизных и контролирующих политик в компании, иначе Lead Time и сложность вывода в ПРОМ для микросервисов останется на уровне монолита. Это сложный момент, т.к. люди консервативны, они привыкают к правилам, и не всегда осознают, что цена ошибки в монолите и в микросервисе разная. Особенно консервативно сопровождение и DBA, работа у них такая, цена ошибки высока) С другой стороны, конечно же микросервисная система должна быть спроектирована так, чтобы падение одного сервиса не приводило к каскадным падениям других. Это снова про критичное влияние архитектуры.
Еще важный момент - в монолите процесс согласования был централизован, им занимались специально обученные люди. С микросервисами релизный процесс попадает в Agile команды. А правило 7 +-2 действует не только для людей, а и для команд. Ну и большое число внешних взаимодействий противоречит Agile-у.
5) к началу разработки нужен работающий и простой DevOps. Разрабочики монолита как правило занимаются только разработкой и если заставить их учиться DevOps - потеряем время. Даже если набирать новых людей - все равно не каждый разработчик готов разрабатывать пайплайны. Т.е. должна быть минимальная конфигурация pipeline в декларативном стиле и работающие CI и CD pipeline. Опять же релизы микросервисов происходят намного чаще, для них это важно.
Также не должно быть невнятных инструкций и десятков настроечных файлов, необходимых для работы pipeline. Конфигурация - это не код, писать его не интересно, разбираться в ней тем более. И это не DevOps на самом деле) Должны быть - генераторы конфигураций, принцип convention over configuration.
6) микросервисам нужно больше инфраструктуры по сравнению с монолитом: сервера, репозитории - и больше доступов. Если в монолите как правило получением инфраструктуры занималась выделенная команда(ы), то при микросервисной архитекторе этим занимается каждая команда. Следовательно, процесс выдачи должен быть максимально автоматизирован и упрощен. Снова работает правило 7+-2. Разработчики должны заниматься тем, что у них лучше всего получается, а не заявками.
#microservices #fuckup #conv_over_conf
На какие грабли можно наступить при распиле монолита на микросервисы?
Небольшой дисклеймер: я подразумеваю, что внедряются микросервисы + Agile, т.к по определению у микросервиса должен быть владелец - команда.
Примеры взяты из жизни)
1) Как я уже говорил https://t.me/javaKotlinDevOps/60 - начинать распил лучше с каких-то небольших частей существующего монолита или новых фичей для этого монолита. Тут важен вот какой момент - скорее всего монолит выполняет некую критическую функцию для компании и, следовательно, для него существуют некие требования. Например, 99,99-ая доступность. И первые "тренировочные" микросервисы не стоит делать для другого сервиса=приложения=монолита, например, потому, что у него более лояльные пользователи или там проще внедрение. Если так сделать, то при переходе от простого монолита к сложному появится куча неожиданных требований по надежности, масштабируемости, ИБ, удобству сопровождения, и архитектуру придется переделывать.
2) должна быть зафиксированная и согласованная со всеми заинтересованными лицами архитектура.
Почему это критично - потому что архитектура - это основа. Можно конечно создавать архитектуру параллельно разработке, по принципу ввяжемся в бой, а там разберемся. Но это приведет к переписыванию микросервисов, к микросервисам-фантомам, которые на бумаге есть, а в жизни оказались не нужны, и к их антиподам, про которых просто забыли и конечно же к проблемам на всех стендах из-за меняющегося API. В конце концов при таком подходе слова архитектура и архитектор по отношению к новой микросервисной платформе станут ругательными)
3) нужно время на обучение разработчиков, тестировщиков и сопровождения. Обучение может быть и должно быть на практике - на реальных сервисах, но оно должно быть учтено в roadmap перехода. Сроки перехода должны быть согласованы командами разработки и сопровождения, а не спущены сверху. Звучит очевидно, но по моему опыту не для всех)
4) нужно изменение релизных и контролирующих политик в компании, иначе Lead Time и сложность вывода в ПРОМ для микросервисов останется на уровне монолита. Это сложный момент, т.к. люди консервативны, они привыкают к правилам, и не всегда осознают, что цена ошибки в монолите и в микросервисе разная. Особенно консервативно сопровождение и DBA, работа у них такая, цена ошибки высока) С другой стороны, конечно же микросервисная система должна быть спроектирована так, чтобы падение одного сервиса не приводило к каскадным падениям других. Это снова про критичное влияние архитектуры.
Еще важный момент - в монолите процесс согласования был централизован, им занимались специально обученные люди. С микросервисами релизный процесс попадает в Agile команды. А правило 7 +-2 действует не только для людей, а и для команд. Ну и большое число внешних взаимодействий противоречит Agile-у.
5) к началу разработки нужен работающий и простой DevOps. Разрабочики монолита как правило занимаются только разработкой и если заставить их учиться DevOps - потеряем время. Даже если набирать новых людей - все равно не каждый разработчик готов разрабатывать пайплайны. Т.е. должна быть минимальная конфигурация pipeline в декларативном стиле и работающие CI и CD pipeline. Опять же релизы микросервисов происходят намного чаще, для них это важно.
Также не должно быть невнятных инструкций и десятков настроечных файлов, необходимых для работы pipeline. Конфигурация - это не код, писать его не интересно, разбираться в ней тем более. И это не DevOps на самом деле) Должны быть - генераторы конфигураций, принцип convention over configuration.
6) микросервисам нужно больше инфраструктуры по сравнению с монолитом: сервера, репозитории - и больше доступов. Если в монолите как правило получением инфраструктуры занималась выделенная команда(ы), то при микросервисной архитекторе этим занимается каждая команда. Следовательно, процесс выдачи должен быть максимально автоматизирован и упрощен. Снова работает правило 7+-2. Разработчики должны заниматься тем, что у них лучше всего получается, а не заявками.
#microservices #fuckup #conv_over_conf
Telegram
(java || kotlin) && devOps
Всем привет!
Поговорим про разделение функционала между микросервисами.
Для начала я бы разделил два случая - распил монолита или создание нового приложения.
При распиле есть несколько путей, которые можно комбинировать:
1) отделять в первую очередь менее…
Поговорим про разделение функционала между микросервисами.
Для начала я бы разделил два случая - распил монолита или создание нового приложения.
При распиле есть несколько путей, которые можно комбинировать:
1) отделять в первую очередь менее…
Всем привет!
Вспомнил еще пару косяков при переходе на микросервисы.
7) не стоит совмещать переход на микросервисы с внедрением новой, необкатанной технологии. Новой не в плане того, что разрабы еще не работали к примеру с k8s. А например такой: использовать какой-нибудь новый "более лучший" аналог Istio или Helm. Или решить отказаться от СУБД в пользу перспективного noSQL решения. Микросервисная система приносит с собой новые технологии, меняются интеграции, DevOps, инфраструктура, скорее всего будет больно при интеграции микросервисов и legacy, не стоит добавлять в процесс еще немного хаоса) В принципе очевидная вещь, но я видел эти грабли в действии)
8) в монолите разработчики ограничены по стеку технологий и по способам реализации фичей и это понятно. Энтропия растет, монолит имеет тенденцию становится "большим куском грязи", поэтому если разрешать каждому добавлять свою любимую библиотеку или делать однотипные вещи по разному - оно будет долго собираться, конфликтовать в runtime, а изучить такой код с нуля станет очень сложно. А одно из преимуществ микросервисов в том, что их можно писать на разных технологических стеках. С другой стороны есть набор эксплуатационных требований, требований ИБ и требований по надежности и отказоустойчивости. Часть из них можно и нужно реализовать снаружи - в k8s или Service Mesh, часть - только внутри. Есть соблазн ту часть, что внутри, вынести в платформенный слой. Это допустимо, хотя и не обязательно - можно просто выставить необходимое платформенное API. Главное помнить, что этот слой должен быть тонким, чтобы его изучение и подключение не стало занимать больше времени, чем разработка фичи)
9) при переходе на микросервисы с большой вероятностью возникнут конфликты между командами разработки монолита и микросервисов. Как их разрешить - вопрос сложный. Как вариант - активно вовлекать монолитные команды в разработку микросервисов. Тут правда возникнет вопрос - а кто будет пилить бизнес-фичи на время перехода? Идеальный кейс - когда команда по очереди занимается и legacy, и микросервисами.
10) если речь про кровавый enterprise, то рядом с монолитами обычно живет ESB - Enterprise Service Bus, по которой монолиты общаются между собой. Может возникнуть идея сделать на основе k8s прямую замену ESB - как единую точку контроля и стандартизации интеграций. Если контроль делать через единый pipeline или проверку правил внутри k8s - ок. А если попытаться отдать разработку интеграций в ту же команду, что занималась ESB - то мы снова получим ESB как единую точку замедления разработки) Важно то, что задачи у k8s и ESB разные, и эту мысль нужно до всех доносить. У k8s - маршрутизация, автоматическое масштабирование, отказоустойчивость, наблюдаемость (observability) и прозрачное шифрование. У ESB - маршрутизация, стандартизация API и конвертация данных. Общая только маршрутизация. Т.е ESB это больше про унификацию бизнес логики, а облако и Service Mesh - про упрощение поддержки микросервисов.
#microservices #fails
Вспомнил еще пару косяков при переходе на микросервисы.
7) не стоит совмещать переход на микросервисы с внедрением новой, необкатанной технологии. Новой не в плане того, что разрабы еще не работали к примеру с k8s. А например такой: использовать какой-нибудь новый "более лучший" аналог Istio или Helm. Или решить отказаться от СУБД в пользу перспективного noSQL решения. Микросервисная система приносит с собой новые технологии, меняются интеграции, DevOps, инфраструктура, скорее всего будет больно при интеграции микросервисов и legacy, не стоит добавлять в процесс еще немного хаоса) В принципе очевидная вещь, но я видел эти грабли в действии)
8) в монолите разработчики ограничены по стеку технологий и по способам реализации фичей и это понятно. Энтропия растет, монолит имеет тенденцию становится "большим куском грязи", поэтому если разрешать каждому добавлять свою любимую библиотеку или делать однотипные вещи по разному - оно будет долго собираться, конфликтовать в runtime, а изучить такой код с нуля станет очень сложно. А одно из преимуществ микросервисов в том, что их можно писать на разных технологических стеках. С другой стороны есть набор эксплуатационных требований, требований ИБ и требований по надежности и отказоустойчивости. Часть из них можно и нужно реализовать снаружи - в k8s или Service Mesh, часть - только внутри. Есть соблазн ту часть, что внутри, вынести в платформенный слой. Это допустимо, хотя и не обязательно - можно просто выставить необходимое платформенное API. Главное помнить, что этот слой должен быть тонким, чтобы его изучение и подключение не стало занимать больше времени, чем разработка фичи)
9) при переходе на микросервисы с большой вероятностью возникнут конфликты между командами разработки монолита и микросервисов. Как их разрешить - вопрос сложный. Как вариант - активно вовлекать монолитные команды в разработку микросервисов. Тут правда возникнет вопрос - а кто будет пилить бизнес-фичи на время перехода? Идеальный кейс - когда команда по очереди занимается и legacy, и микросервисами.
10) если речь про кровавый enterprise, то рядом с монолитами обычно живет ESB - Enterprise Service Bus, по которой монолиты общаются между собой. Может возникнуть идея сделать на основе k8s прямую замену ESB - как единую точку контроля и стандартизации интеграций. Если контроль делать через единый pipeline или проверку правил внутри k8s - ок. А если попытаться отдать разработку интеграций в ту же команду, что занималась ESB - то мы снова получим ESB как единую точку замедления разработки) Важно то, что задачи у k8s и ESB разные, и эту мысль нужно до всех доносить. У k8s - маршрутизация, автоматическое масштабирование, отказоустойчивость, наблюдаемость (observability) и прозрачное шифрование. У ESB - маршрутизация, стандартизация API и конвертация данных. Общая только маршрутизация. Т.е ESB это больше про унификацию бизнес логики, а облако и Service Mesh - про упрощение поддержки микросервисов.
#microservices #fails
Всем привет!
После прочтения новости о новых методах создания коллекций в JDK 19 https://inside.java/2022/10/24/sip069/ сразу возникает вопрос - зачем было ждать 17 версий, почему не выпустили раньше???)))
Думаю сэкономили бы сотни, возможно тысячи человекодней, потраченных на отладку. А фича на самом деле плевая.
Задачу в 17 году завели, кстати - https://bugs.openjdk.org/browse/JDK-8186958
Из подобных очевидно полезных, но появившихся очень поздно фичей вспоминается JDK 14 и человекочитаемое сообщение об NPE.
#java
После прочтения новости о новых методах создания коллекций в JDK 19 https://inside.java/2022/10/24/sip069/ сразу возникает вопрос - зачем было ждать 17 версий, почему не выпустили раньше???)))
Думаю сэкономили бы сотни, возможно тысячи человекодней, потраченных на отладку. А фича на самом деле плевая.
Задачу в 17 году завели, кстати - https://bugs.openjdk.org/browse/JDK-8186958
Из подобных очевидно полезных, но появившихся очень поздно фичей вспоминается JDK 14 и человекочитаемое сообщение об NPE.
#java
inside.java
Pre-Allocated Methods for HashMap and HashSet - Sip of Java
Learn the benefits of using the new pre-allocated methods for HashMap and HashSet.
И снова привет!
По поводу недавней уязвимости в Apache Commons Text https://habr.com/ru/company/pt/blog/694720/ у меня две мысли:
1) техника очень похожа на Log4Shell https://ru.wikipedia.org/wiki/Log4Shell, с которой и начался этот блог - и там, и там интерполяция выражений в текстовых строках с далеко идущими последствиями. Who is next?)))
2) лично я узнал о возможности интерполяции в обоих случаях после появления соответствующих уязвимостей))))
#security #vulnerability #java
По поводу недавней уязвимости в Apache Commons Text https://habr.com/ru/company/pt/blog/694720/ у меня две мысли:
1) техника очень похожа на Log4Shell https://ru.wikipedia.org/wiki/Log4Shell, с которой и начался этот блог - и там, и там интерполяция выражений в текстовых строках с далеко идущими последствиями. Who is next?)))
2) лично я узнал о возможности интерполяции в обоих случаях после появления соответствующих уязвимостей))))
#security #vulnerability #java
Хабр
Из-за чего весь сыр-бор: про уязвимость Text4Shell
За последнюю неделю в сфере инфобеза стали появляться новости о втором пришествии уязвимости Log4Shell, получившей название Text4Shell . Первым об уязвимости сообщил Alvaro Muñoz, который рассказал о...
Всем привет!
По следам отличной книги Владимира Хорикова Принципы юнит-тестирования, см https://t.me/javaKotlinDevOps/50, решил погрузится в тему и прочитать еще и классику жанра - Шаблоны тестирования xUnit. Рефакторинг кода тестов https://www.litres.ru/dzherard-mesarosh/shablony-testirovaniya-xunit-refaktoring-koda-t-48637685/
К слову, на английском книга выложена в виде сайта со списком паттернов тестирования http://xunitpatterns.com/
По сути книга и является большим справочников паттернов. Автор - человек дотошный: любую типовую практику, используемую для написания тестов он вынес в шаблон с подробным описанием истории возникнования, вариантов релизации и примеров, мотивирующих на использование шаблонов.
Вот примеры шаблонов из книги, на которые хотел бы обратить внимание:
1) 5 видов моков http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html - от Dummy object, который ничего не возвращает, не делает и не проверяет, а нужен лишь для передачи как параметр, до Fake Object - облегченной реализации боевой логики, без обращений во внешние системы и БД. Видел ссылки на эти 5 типов в других статьях и книгах.
2) Test Utility Method - одна из базовых практик при рефаторинге тестов, вынесение повторяющего кода в отдельные методы. Это может быть код настройки системы, код проверки результата с assert-ми и код очистики ресурсов. Очень полезная штука, с какого-то момента после рефакторинга вынесенных методов для их переиспользования мы получаем некий тестовый язык, по сути свой DSL, и это без всяких Cucumber-ов и прочих BDD. http://xunitpatterns.com/Test%20Utility%20Method.html
Остальное не влезло в пост телеги, см. на дзене https://dzen.ru/media/id/62fe543369f7d3314503a87d/recenziia-na-shablony-testirovaniia-xunit-6367bbba7d9dc52b40aa3ff3
#unittests #books
По следам отличной книги Владимира Хорикова Принципы юнит-тестирования, см https://t.me/javaKotlinDevOps/50, решил погрузится в тему и прочитать еще и классику жанра - Шаблоны тестирования xUnit. Рефакторинг кода тестов https://www.litres.ru/dzherard-mesarosh/shablony-testirovaniya-xunit-refaktoring-koda-t-48637685/
К слову, на английском книга выложена в виде сайта со списком паттернов тестирования http://xunitpatterns.com/
По сути книга и является большим справочников паттернов. Автор - человек дотошный: любую типовую практику, используемую для написания тестов он вынес в шаблон с подробным описанием истории возникнования, вариантов релизации и примеров, мотивирующих на использование шаблонов.
Вот примеры шаблонов из книги, на которые хотел бы обратить внимание:
1) 5 видов моков http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html - от Dummy object, который ничего не возвращает, не делает и не проверяет, а нужен лишь для передачи как параметр, до Fake Object - облегченной реализации боевой логики, без обращений во внешние системы и БД. Видел ссылки на эти 5 типов в других статьях и книгах.
2) Test Utility Method - одна из базовых практик при рефаторинге тестов, вынесение повторяющего кода в отдельные методы. Это может быть код настройки системы, код проверки результата с assert-ми и код очистики ресурсов. Очень полезная штука, с какого-то момента после рефакторинга вынесенных методов для их переиспользования мы получаем некий тестовый язык, по сути свой DSL, и это без всяких Cucumber-ов и прочих BDD. http://xunitpatterns.com/Test%20Utility%20Method.html
Остальное не влезло в пост телеги, см. на дзене https://dzen.ru/media/id/62fe543369f7d3314503a87d/recenziia-na-shablony-testirovaniia-xunit-6367bbba7d9dc52b40aa3ff3
#unittests #books
Telegram
(java || kotlin) && devOps
Всем привет! Ещё одно ревью на книгу, которую упоминал: Владимир Хориков Принципы юнит-тестирования. https://habr.com/ru/company/piter/blog/528872/ Можно сказать уникальная книга про то, как писать юнит тесты. Я знаю таких всего две - эту и Шаблоны тестирования…
Всем привет!
Нашел пару статей на тему автоматической валидации настроек Spring Boot, как средствами IDEA, так и в runtime. Работает для yaml и properties форматов.
Для авподополнения в IDEA используется spring-boot-configuration-processor, который сканируется файлы с Spring аннотацией @Configuration и формирует необходимые метаднные. Также можно создавать руками, полезно для чужого кода
https://www.baeldung.com/intellij-resolve-spring-boot-configuration-properties
В runtime это делается через Jacarta Bean Validation API https://beanvalidation.org/, самая известная реализация Hibernate Validator https://hibernate.org/validator/ Кстати, несмотря на название к ORM библиотека прямого отношения не имеет.
Детали см. https://habr.com/ru/post/505628/
Если не хватает возможностей аннотаций из Validation API можно даже валидацию в коде описать, с помощью Spring Validator https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/validation/Validator.html
Как по мне - полезная штука, рекомендую использовать, позволяет быстрее обнаружить ошибки.
#spring_boot #java #IDEA #validation
Нашел пару статей на тему автоматической валидации настроек Spring Boot, как средствами IDEA, так и в runtime. Работает для yaml и properties форматов.
Для авподополнения в IDEA используется spring-boot-configuration-processor, который сканируется файлы с Spring аннотацией @Configuration и формирует необходимые метаднные. Также можно создавать руками, полезно для чужого кода
https://www.baeldung.com/intellij-resolve-spring-boot-configuration-properties
В runtime это делается через Jacarta Bean Validation API https://beanvalidation.org/, самая известная реализация Hibernate Validator https://hibernate.org/validator/ Кстати, несмотря на название к ORM библиотека прямого отношения не имеет.
Детали см. https://habr.com/ru/post/505628/
Если не хватает возможностей аннотаций из Validation API можно даже валидацию в коде описать, с помощью Spring Validator https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/validation/Validator.html
Как по мне - полезная штука, рекомендую использовать, позволяет быстрее обнаружить ошибки.
#spring_boot #java #IDEA #validation
Baeldung
IntelliJ – Cannot Resolve Spring Boot Configuration Properties Error | Baeldung
IntelliJ can provide autocomplete and context help for custom properties, but we need to make some additional configuration to our project to enable that.
Всем привет!
Сегодня расскажу о нескольких неочевидных особенностях работы Maven.
1) за каждый этап жизненного цикла сборки Maven отвечает какой-то плагин. Один плагин может покрывать несколько этапов. Это не новость. Я надеюсь) Интересно то, что эти плагины не обязательно указывать в pom, вот минимальный рабочий pom, с которым ваш проект скомпилируется - https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Minimal_POM
Работает принцип convention over configuration.
А вот описание алгоритма определения нужного плагина: https://maven.apache.org/guides/introduction/introduction-to-plugin-prefix-mapping.html
Перескажу его вкратце.
Если вы указали при сборке фазу жизненного цикла, например, maven install, то определяются все необходимые шаги сборки и требуемые плагины как описано вот тут https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
А далее Maven ищет специальные файлы maven-metadata во всех подключенных к проекту репо, сливает их в один и ищет плагин по атрибуту prefix, который равен указаному вот тут как plugin https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#default-lifecycle-bindings-packaging-ejb-ejb3-jar-par-rar-war
Вот пример базового maven-metadata из Maven Central https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Как можно заметить, у большинства плагинов, поисковый префикс является частью artefactId. Это соглашение о наименовании, хотя и не обязательное. Т.е. всегда можно сделать свой maven-metadata-local.xml и привязать нужный плагин к нужной фазе там. Но я бы не рекомендовал так делать, т.к. это завязка на конкретный сборщик, которая хранится отдельно от кода вашего проекта. Также при создании плагина можно назвать его как угодно и указать к какой фазе сборки он привязан, может быть полезно, когда одним словом сложно выразить задачу плагина.
Если вы явно указываете цель для сборки, например, compiler:compile, то выполняется только эта цель, Maven пропускает разбор жизненного цикла сборки, сразу переходит к поиску нужного плагина в maven-metadata, в данном случае по префиксу compiler.
Вопрос - а как Maven определяет версию плагина? Есть еще один maven-metadata файлик с версиями у каждого плагина https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml
И как раз здесь лежит подводный камень: когда Maven-у понадобится конкретный плагин - он возьмет последнюю версию из тех, что найдет в подключенных к проекту репозиториях. А брать последнюю версию не всегда хорошо - там могут быть баги, или она просто может криво закачаться, если речь про внутренний прокси репозиторий.
Поэтому рекомендую начинать с минимальным POM, а перед выходом на ПРОМ фиксировать все версии плагинов.
Еще может возникнуть вопрос - а нельзя ли этот фокус провернуть с неофициальными плагинами? Ответ - можно, нужно лишь указать в settings.xml где еще искать maven-metadata.xml:
<pluginGroups>
<pluginGroup>org.codehaus.modello</pluginGroup>
</pluginGroups>
Еще момент - всегда можно добавить в pom конфигурацию плагина и там переопределить фазу сборки, на которой он запустится.
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
В примере цель из плагина jacoco-maven-plugin привяза к фазе test.
Выше я описывал, как работает Maven в режиме convention over configuration.
Продолжение следует...
#maven #buildtool #conv_over_conf
Сегодня расскажу о нескольких неочевидных особенностях работы Maven.
1) за каждый этап жизненного цикла сборки Maven отвечает какой-то плагин. Один плагин может покрывать несколько этапов. Это не новость. Я надеюсь) Интересно то, что эти плагины не обязательно указывать в pom, вот минимальный рабочий pom, с которым ваш проект скомпилируется - https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Minimal_POM
Работает принцип convention over configuration.
А вот описание алгоритма определения нужного плагина: https://maven.apache.org/guides/introduction/introduction-to-plugin-prefix-mapping.html
Перескажу его вкратце.
Если вы указали при сборке фазу жизненного цикла, например, maven install, то определяются все необходимые шаги сборки и требуемые плагины как описано вот тут https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
А далее Maven ищет специальные файлы maven-metadata во всех подключенных к проекту репо, сливает их в один и ищет плагин по атрибуту prefix, который равен указаному вот тут как plugin https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#default-lifecycle-bindings-packaging-ejb-ejb3-jar-par-rar-war
Вот пример базового maven-metadata из Maven Central https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-metadata.xml
Как можно заметить, у большинства плагинов, поисковый префикс является частью artefactId. Это соглашение о наименовании, хотя и не обязательное. Т.е. всегда можно сделать свой maven-metadata-local.xml и привязать нужный плагин к нужной фазе там. Но я бы не рекомендовал так делать, т.к. это завязка на конкретный сборщик, которая хранится отдельно от кода вашего проекта. Также при создании плагина можно назвать его как угодно и указать к какой фазе сборки он привязан, может быть полезно, когда одним словом сложно выразить задачу плагина.
Если вы явно указываете цель для сборки, например, compiler:compile, то выполняется только эта цель, Maven пропускает разбор жизненного цикла сборки, сразу переходит к поиску нужного плагина в maven-metadata, в данном случае по префиксу compiler.
Вопрос - а как Maven определяет версию плагина? Есть еще один maven-metadata файлик с версиями у каждого плагина https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/maven-metadata.xml
И как раз здесь лежит подводный камень: когда Maven-у понадобится конкретный плагин - он возьмет последнюю версию из тех, что найдет в подключенных к проекту репозиториях. А брать последнюю версию не всегда хорошо - там могут быть баги, или она просто может криво закачаться, если речь про внутренний прокси репозиторий.
Поэтому рекомендую начинать с минимальным POM, а перед выходом на ПРОМ фиксировать все версии плагинов.
Еще может возникнуть вопрос - а нельзя ли этот фокус провернуть с неофициальными плагинами? Ответ - можно, нужно лишь указать в settings.xml где еще искать maven-metadata.xml:
<pluginGroups>
<pluginGroup>org.codehaus.modello</pluginGroup>
</pluginGroups>
Еще момент - всегда можно добавить в pom конфигурацию плагина и там переопределить фазу сборки, на которой он запустится.
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
В примере цель из плагина jacoco-maven-plugin привяза к фазе test.
Выше я описывал, как работает Maven в режиме convention over configuration.
Продолжение следует...
#maven #buildtool #conv_over_conf
maven.apache.org
Introduction to the POM – Maven
Всем привет!
Продолжим про Maven.
2) Несмотря на то, что к каждой фазе жизненного цикла по умолчанию привязана одна цель (goal) из конкретного плагина, на каждую фазу можно повесить сколько угодно целей. Порядок выполнения - как указано в pom.xml. Это еще одна одна причина, почему полезно явно обьявлять плагины в pom.
3) Если у вас в settings.xml указано несколько репозиториев с артифактами, и поиск по всем не дал результата - будет ошибка, но с некорректным текстом. В ней будет сказано, что артифакта нет в первом по списку репозитории
4) Если какие-то из репозиториев в settings.xml требуют аутентификации, данные для аутентификации были указаны и протухли, то вы узнаете об этом только тогда, когда понадобится обновить библиотеку, которая есть только в этом репозитории. Ошибка с аутентификацией будет в логах каждой сборки, но сборку не ломает
5) Может возникнуть вопрос - а зачем Maven каждый раз ходит по удаленным репозиториям, если зависимости не менялись. Это поведение контролируется параметром репозитория updatePolicy в settings.xml:
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
<releases>
<updatePolicy>always</updatePolicy>
</releases>
</repository>
Возможные значения: "always", "daily" (default), "interval:XXX" (in minutes) or "never".
Можно установить never, но есть два риска:
а) не будут подтягиваться новые snapshot,
б) не будут подтягиваться новые версии уже закачанных артифактов. А эту информацию индексирует IDEA и использует при AutoComplition
6) Если сохраненные локально зависимости были повреждены, то принудительно их перезагрузить можно через очистку локального репозитория, который находится в папке .m2. Командой mvn dependency:purge-local-repository, подробнее тут https://www.baeldung.com/maven-force-update или просто грохнув нужную папку в .m2
7) согласно уже упоминаемому ранее принципу convention over configuration по умолчанию в список репозиториев неявно включается Maven Central. Подробнее про settings.xml и его опции можно почитать тут https://habr.com/ru/post/339902/
8) список репозиториев можно задать как в pom.xml, так и в settings.xml. Предпочтительнее второй вариант, особенно при разработке библиотек, особенно внутренних. Причина: адрес репозитория может поменяться, а settings.xml проще обновить централизованно. Не говоря уже про библиотеки, которые придется перевыпускать. Конечно, если вы ходили по ссылке из предыдущего пункта - у нас есть возможность переопределить любой репозиторий из pom через зеркала\mirror в settings.xml. Причем при настройке зеркала можно не только указывать явно заменяемый репозиторий, но и использовать операторы * и ! Но это все же костыль)
#maven #buildtool #conv_over_conf
Продолжим про Maven.
2) Несмотря на то, что к каждой фазе жизненного цикла по умолчанию привязана одна цель (goal) из конкретного плагина, на каждую фазу можно повесить сколько угодно целей. Порядок выполнения - как указано в pom.xml. Это еще одна одна причина, почему полезно явно обьявлять плагины в pom.
3) Если у вас в settings.xml указано несколько репозиториев с артифактами, и поиск по всем не дал результата - будет ошибка, но с некорректным текстом. В ней будет сказано, что артифакта нет в первом по списку репозитории
4) Если какие-то из репозиториев в settings.xml требуют аутентификации, данные для аутентификации были указаны и протухли, то вы узнаете об этом только тогда, когда понадобится обновить библиотеку, которая есть только в этом репозитории. Ошибка с аутентификацией будет в логах каждой сборки, но сборку не ломает
5) Может возникнуть вопрос - а зачем Maven каждый раз ходит по удаленным репозиториям, если зависимости не менялись. Это поведение контролируется параметром репозитория updatePolicy в settings.xml:
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
<releases>
<updatePolicy>always</updatePolicy>
</releases>
</repository>
Возможные значения: "always", "daily" (default), "interval:XXX" (in minutes) or "never".
Можно установить never, но есть два риска:
а) не будут подтягиваться новые snapshot,
б) не будут подтягиваться новые версии уже закачанных артифактов. А эту информацию индексирует IDEA и использует при AutoComplition
6) Если сохраненные локально зависимости были повреждены, то принудительно их перезагрузить можно через очистку локального репозитория, который находится в папке .m2. Командой mvn dependency:purge-local-repository, подробнее тут https://www.baeldung.com/maven-force-update или просто грохнув нужную папку в .m2
7) согласно уже упоминаемому ранее принципу convention over configuration по умолчанию в список репозиториев неявно включается Maven Central. Подробнее про settings.xml и его опции можно почитать тут https://habr.com/ru/post/339902/
8) список репозиториев можно задать как в pom.xml, так и в settings.xml. Предпочтительнее второй вариант, особенно при разработке библиотек, особенно внутренних. Причина: адрес репозитория может поменяться, а settings.xml проще обновить централизованно. Не говоря уже про библиотеки, которые придется перевыпускать. Конечно, если вы ходили по ссылке из предыдущего пункта - у нас есть возможность переопределить любой репозиторий из pom через зеркала\mirror в settings.xml. Причем при настройке зеркала можно не только указывать явно заменяемый репозиторий, но и использовать операторы * и ! Но это все же костыль)
#maven #buildtool #conv_over_conf
Baeldung
Force Repository Update with Maven | Baeldung
Explore the options and goals of Maven that forcibly updates our local repository.
Всем привет!
Меня всегда поражала и даже не побоюсь этого слова восхищала гибкость развитых языков разработки, в частности Java.
Пример. Есть достаточно редкая проблема - как протестировать код, вызывающий System.exit().
У меня до сих пор такой необходимости не возникало, но понимаю кейсы, где такой код может быть.
Как думаете - можно протестировать? Конечно же да.
А сколько вариантов перехватить вызов?
...
Три!
https://www.baeldung.com/junit-system-exit
И это только технические решения, без учета рефакторинга кода с выделением в отдельный метод и созданием тестового двойника.
А рефакторить тоже можно по разному...
#java
Меня всегда поражала и даже не побоюсь этого слова восхищала гибкость развитых языков разработки, в частности Java.
Пример. Есть достаточно редкая проблема - как протестировать код, вызывающий System.exit().
У меня до сих пор такой необходимости не возникало, но понимаю кейсы, где такой код может быть.
Как думаете - можно протестировать? Конечно же да.
А сколько вариантов перехватить вызов?
...
Три!
https://www.baeldung.com/junit-system-exit
И это только технические решения, без учета рефакторинга кода с выделением в отдельный метод и созданием тестового двойника.
А рефакторить тоже можно по разному...
#java
Baeldung
JUnit – Testing Methods That Call System.exit() | Baeldung
Learn techniques for testing methods that call System.exit() when using JUnit.
Всем привет!
Прочитал еще одну книжку: "Распределенные системы. Паттерны проектирования" https://habr.com/ru/company/piter/blog/442514/
Главное, что хочу отметить: в книге никакого хардкора нет, читается легко.
Еще плюс - в конце многих глав есть простые примеры как развернуть реализацию паттерна в k8s. Можно и нужно экспериментировать)
Что характерно - автор участвовал в создании k8s, поэтому почти все примеры разворачиваются в k8s, даже Redis и Kafka.
В книге 3 части.
Паттерны уровня pod-а. Спойлер - речь про sidecar-ы.
sidecar-ы делятся по назначению:
1) добавление нового функционала к основному сервису - это собственно sidecar
2) прокси к шардированному сервису - Ambassador. Шардированному - это важно, в этом случае нельзя просто разбросать запросы по round robin, как может сделать Service из k8s
3) адаптация формата данных основного сервиса - Adapter.
Всегда думал, что сайдкар это просто сайдкар, а оказывается есть 3 вида)
Паттерны проектирования обслуживающих систем. Название странное, да)
Здесь сборная солянка из репликации, шардирования, алгоритма выбора лидера и Function As A Service (FaaS).
Репликация - это дублирование для отказоустойчивости и производительности, шардирование - создание отдельных экземпляров для обработки части данных, если весь набор не вмещается на одной машине. Можно совмещать оба подхода. Для это есть отдельный паттерн Scatter\Gather, распараллеливающий обработку "тяжелой" задачи на несколько серверов, после чего агрегирующий результат. Пример: поиск по большому объему текста. Есть хорошее замечание, что накладные расходы на распараллеливание растут с числом потоков и имеется предел, после которого ускорения не будет.
Хорошие примеры по каким критериям можно шардировать данные.
"На пальцах" описано выбор лидера на примере распределенных блокировок с помощью etcd. etcd к слову используется в k8s как хранилище состояния кластера.
Хорошо аргументировано когда полезен подход FaaS. Спойлер - когда запросов мало и для них дорого держать сервис постоянно работающим.
Третья часть - паттерны потоковой обработки. Потоковая обработка - это асинхронные часто многошаговые задачи, когда ответ в режиме онлайн не требуется. Сейчас продвигается их замена на стримингом на основе Kafka, Kafka Streams и Spark, который способен выдать ответ в режиме онлайн, но есть кейсы, когда это не нужно. Типичный пример потоковой задач: периодическое построение отчетов, периодическая выгрузка данных.
Обозреваемые паттерны: Copier (параллельная обработка с разными параметрами, например, рендеринг видео с разных разрешениях), Merger (объединение нескольких очередей в одну), Filter (отбрасывание части задач), Splitter (разделение потока в зависимости от настроек задачи: уведомление клиента по sms или по email), Sharder (аналог Map в Map\Reduce, параллельная обработка), Reduce (объединение с агрегацией), Join (объединение с ожиданием всех).
Маленькое замечание: паттерны из последней части применимы не только в облаке, но и при многопоточке в Java. Книжка про распределённые системы, поэтому в ней любая функция выносится в облако. Нужно ли так делать всегда - нет. См. https://t.me/javaKotlinDevOps/59 Ключевые моменты: разные команды, возможность переиспользования, сильно различающийся функционал.
Еще интересный момент - приводится пример использования Job-ов из k8s. Пример хороший, но важной замечание - со Spring-ом так делать не рекомендую, сервис, который запускается 1 минуту, работает 1 минуту и останавливается - не рационально.
Также отдельный большой вопрос - нужно ли все "пихать" в облако. Краткий ответ - нет. Да, облако дает возможность хранить данные: см. StatefullSet https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/. Да, облако, особенно c Service mesh, дает продвинутые возможности маршрутизации. Но часто репликация и шардирование данных настолько сложны, что облачные механизмы тут не помогут, а тогда возникает вопрос - а нужно ли в таких случаях облако вообще?
В целом книжку могу рекомендовать если есть свободное время и желание изучить примеры реализации распределенных систем.
#books
Прочитал еще одну книжку: "Распределенные системы. Паттерны проектирования" https://habr.com/ru/company/piter/blog/442514/
Главное, что хочу отметить: в книге никакого хардкора нет, читается легко.
Еще плюс - в конце многих глав есть простые примеры как развернуть реализацию паттерна в k8s. Можно и нужно экспериментировать)
Что характерно - автор участвовал в создании k8s, поэтому почти все примеры разворачиваются в k8s, даже Redis и Kafka.
В книге 3 части.
Паттерны уровня pod-а. Спойлер - речь про sidecar-ы.
sidecar-ы делятся по назначению:
1) добавление нового функционала к основному сервису - это собственно sidecar
2) прокси к шардированному сервису - Ambassador. Шардированному - это важно, в этом случае нельзя просто разбросать запросы по round robin, как может сделать Service из k8s
3) адаптация формата данных основного сервиса - Adapter.
Всегда думал, что сайдкар это просто сайдкар, а оказывается есть 3 вида)
Паттерны проектирования обслуживающих систем. Название странное, да)
Здесь сборная солянка из репликации, шардирования, алгоритма выбора лидера и Function As A Service (FaaS).
Репликация - это дублирование для отказоустойчивости и производительности, шардирование - создание отдельных экземпляров для обработки части данных, если весь набор не вмещается на одной машине. Можно совмещать оба подхода. Для это есть отдельный паттерн Scatter\Gather, распараллеливающий обработку "тяжелой" задачи на несколько серверов, после чего агрегирующий результат. Пример: поиск по большому объему текста. Есть хорошее замечание, что накладные расходы на распараллеливание растут с числом потоков и имеется предел, после которого ускорения не будет.
Хорошие примеры по каким критериям можно шардировать данные.
"На пальцах" описано выбор лидера на примере распределенных блокировок с помощью etcd. etcd к слову используется в k8s как хранилище состояния кластера.
Хорошо аргументировано когда полезен подход FaaS. Спойлер - когда запросов мало и для них дорого держать сервис постоянно работающим.
Третья часть - паттерны потоковой обработки. Потоковая обработка - это асинхронные часто многошаговые задачи, когда ответ в режиме онлайн не требуется. Сейчас продвигается их замена на стримингом на основе Kafka, Kafka Streams и Spark, который способен выдать ответ в режиме онлайн, но есть кейсы, когда это не нужно. Типичный пример потоковой задач: периодическое построение отчетов, периодическая выгрузка данных.
Обозреваемые паттерны: Copier (параллельная обработка с разными параметрами, например, рендеринг видео с разных разрешениях), Merger (объединение нескольких очередей в одну), Filter (отбрасывание части задач), Splitter (разделение потока в зависимости от настроек задачи: уведомление клиента по sms или по email), Sharder (аналог Map в Map\Reduce, параллельная обработка), Reduce (объединение с агрегацией), Join (объединение с ожиданием всех).
Маленькое замечание: паттерны из последней части применимы не только в облаке, но и при многопоточке в Java. Книжка про распределённые системы, поэтому в ней любая функция выносится в облако. Нужно ли так делать всегда - нет. См. https://t.me/javaKotlinDevOps/59 Ключевые моменты: разные команды, возможность переиспользования, сильно различающийся функционал.
Еще интересный момент - приводится пример использования Job-ов из k8s. Пример хороший, но важной замечание - со Spring-ом так делать не рекомендую, сервис, который запускается 1 минуту, работает 1 минуту и останавливается - не рационально.
Также отдельный большой вопрос - нужно ли все "пихать" в облако. Краткий ответ - нет. Да, облако дает возможность хранить данные: см. StatefullSet https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/. Да, облако, особенно c Service mesh, дает продвинутые возможности маршрутизации. Но часто репликация и шардирование данных настолько сложны, что облачные механизмы тут не помогут, а тогда возникает вопрос - а нужно ли в таких случаях облако вообще?
В целом книжку могу рекомендовать если есть свободное время и желание изучить примеры реализации распределенных систем.
#books
Хабр
Распределенные системы. Паттерны проектирования. Обзор книги
Здравствуйте, коллеги. Сегодня мы публикуем перевод очередного обзора с сайта Бена Нейдела — этот сайт наверняка заинтересует вас и в оригинале. На сей раз мы поговорим о книге " Распределенные...
Небольшой оффтоп. Т.к. книжка простая, думал, что напишу пост за 10 минут. По факту неделю не мог собраться с мыслями) Точнее не мог для себя сформулировать, что же я из нее вынес. Книжка же не вода, в целом полезная. Сложно писать о простом)))
Интересно, чем им доклад не угодил?) https://highload.ru/moscow/2022/abstracts/8286 Я бы послушал)
highload.ru
Алексей Салмин на HighLoad++ 2022
В этом докладе я расскажу краткую историю развития ядра веб-поиска Яндекса за последние несколько лет. Основной задачей команды, которая разрабатывает наш движок, можно назвать экономию ресурсов. Экономия не является самостоятельной целью, но при этом имеет…