Всем привет!
Снова про алгоритмы.
Представим ситуацию, когда для каждого нашего объекта нужно получить адрес, где его хранить. Это может быть сервер в случае шардированной БД или корзина в HashMap. Хорошим решением является функция, которая однозначно по уникальному идентификатору нашей сущности выдаёт адрес сервера. Альтернативой является хранение таблицы соответствия отдельно, в случае распределённой системы это будет отдельный микросервис, что означает +1 запрос и увеличение задержек. Тривиальной реализацией является простой алгоритм - остаток от деления на число серверов. Но у такого есть минус - при изменении числа серверов распределение объектов по серверам или корзинам сильно меняется. Мы же хотим равномерное распределение. Если перестроение HashMap в памяти обычно (!) не проблема, то в случае десятка серверов в сети - ещё какая проблема. Для решения этой проблемы есть алгоритм консистентного хэширования. Он не защищает от перераспределения данных, но фиксирует их размер.
https://jaidayo.livejournal.com/2645.html #algorithm #distributedsystem
P.S. HashMap данный алгоритм не использует, на размерах порядка 1 млн записей рекомендуется сразу задавать начальный размер равный предполагаемому умноженному на 1.5
P.P.S. Хозяйке на заметку: в nginx, при использовании его в качестве балансировщика включить консистентное вычисление хэша от URL запроса можно одной строкой hash $request_uri consistent
P...S А вот документация по использованию алгоритма в Openstack https://docs.openstack.org/swift/latest/ring_background.html
#algorithm
Снова про алгоритмы.
Представим ситуацию, когда для каждого нашего объекта нужно получить адрес, где его хранить. Это может быть сервер в случае шардированной БД или корзина в HashMap. Хорошим решением является функция, которая однозначно по уникальному идентификатору нашей сущности выдаёт адрес сервера. Альтернативой является хранение таблицы соответствия отдельно, в случае распределённой системы это будет отдельный микросервис, что означает +1 запрос и увеличение задержек. Тривиальной реализацией является простой алгоритм - остаток от деления на число серверов. Но у такого есть минус - при изменении числа серверов распределение объектов по серверам или корзинам сильно меняется. Мы же хотим равномерное распределение. Если перестроение HashMap в памяти обычно (!) не проблема, то в случае десятка серверов в сети - ещё какая проблема. Для решения этой проблемы есть алгоритм консистентного хэширования. Он не защищает от перераспределения данных, но фиксирует их размер.
https://jaidayo.livejournal.com/2645.html #algorithm #distributedsystem
P.S. HashMap данный алгоритм не использует, на размерах порядка 1 млн записей рекомендуется сразу задавать начальный размер равный предполагаемому умноженному на 1.5
P.P.S. Хозяйке на заметку: в nginx, при использовании его в качестве балансировщика включить консистентное вычисление хэша от URL запроса можно одной строкой hash $request_uri consistent
P...S А вот документация по использованию алгоритма в Openstack https://docs.openstack.org/swift/latest/ring_background.html
#algorithm
Livejournal
Консистентные хэши (Consistent hashing)
View more presentations from Anton. Download here. Зачем? Есть большой пласт технологий для создания распределенных систем: создание железных решений, распределенного ПО для управления системой, распределенных хранилищь, etc. Все это двигает grid технологии…
Всем привет. Немного про цену создания объектов в Java. Для короткоживущих объектов на последних версиях JVM выигрыш от переиспользования объектов про сравнению с созданием составляет пример 25%. Справедливости ради на Java 8 разница была в 40%, т.е garbage collection развивается. Описание эксперимента тут http://blog.vanillajava.blog/2022/09/java-is-very-fast-if-you-dont-create.html #java #performance
blog.vanillajava.blog
Java is Very Fast, If You Don’t Create Many Objects
You still have to watch how many objects you create. This article looks at a benchmark passing events over TCP/IP at 4 billion events per ...
С другой стороны Java = объекты. Сборщики мусора стали достаточно умными, чтобы запускаться только тогда, когда памяти не хватает, и достаточно быстро убирать короткоживущихе объекты. Можно выбрать сборщик мусора либо с минимальными паузами, либо с минимальным overhead по ресурсам. Про выбор можно почитать тут https://www.baeldung.com/java-choosing-gc-algorithm Перераспределение памяти по ходу работы программы и роста heap можно убрать установив одинаковые xms и xmx, тогда JRE заберёт эту память из системы «навсегда» при старте приложения. Если на сервере много памяти и вы уверены, что heap-а точно хватит на все время работы программы - есть фейковый GC, который имеет ровно одну фичу - падать когда память кончается) https://openjdk.org/jeps/318 Когда это все не работает - примерно на десятках миллионах RPS как в статье из примера выше) #java #gc #performance
Всем привет! Уже упоминал книгу Идеальная работа Роберта Мартина https://habr.com/ru/company/piter/blog/679378/ Дочитал до конца и вот более полный отзыв. Вначале о хорошем: главы про применение TDD на практических задачах с livecoding - must read. Хороша глава про принцип YAGNI - you aren't gonna need it, особенно про то, как скорость современных компьютеров сделала этот принцип более актуальным. Хорошо написано про структуру presentation layer для упрощения тестирования этого слоя. Шаблоны тестирования там же. Хорошо про вязкость процесса программирования - скорость сборки и прогона тестов. Интересные данные про влияние парного программирования на скорость разработки. Только из-за этого книгу могу рекомендовать к прочтению. Но есть на мой взгляд и минус. Сквозь всю книгу проходит тезис про ответственность программиста за код. Вообще говоря мысль правильная. За любую работу нужно отвечать и делать ее качественно. Смущают три момента. 1) пафос, с которым это подаётся. Сразу вспоминается «Даешь пятилетку за четыре года». Или такой персонаж как Тони Робинсон. Делай хорошо, не делай плохо. Да, все тезисы обоснованы, но я бы добавил больше юмора, практических примеров и меньше пафоса. 2) ко многим вещам ты приходишь с опытом. Поэтому требовать одинаково с сеньора и джуна нельзя. Автор про это говорит, один раз, и все. Для кого тогда эти главы? 3) для доказательства хороших идей подтасовки не нужны. Пример с программистами из Volkswagen, которые обманули программу измерения выхлопов, выглядит именно подтасовкой. Вкратце - чтобы пройти новый экологический стандарт для дизельных двигателей руководство компании дало указание разработчикам так настроить программу работы двигателя, чтобы в тестовом режиме все было ок, хотя по факту он выделял больше вредных веществ, чем разрешено. Но штука в том, что с внедрением этого стандарта старые машины продолжают ездить по улицам. И писать что из-за плохих программистов машины причиняют вред здоровью людей... ну такое. Но, повторюсь - читайте главы про TDD, простоту кода, тестирования БД и UI, парное программирование и настройку проекта. #books #tdd
Хабр
Идеальная работа. Программирование без прикрас
Привет, Хаброжители! В книге «Идеальная работа. Программирование без прикрас» легендарный Роберт Мартин (Дядюшка Боб) создал исчерпывающее руководство по хорошей работе для каждого программиста....
Всем привет! Ещё одно ревью на книгу, которую упоминал: Владимир Хориков Принципы юнит-тестирования. https://habr.com/ru/company/piter/blog/528872/ Можно сказать уникальная книга про то, как писать юнит тесты. Я знаю таких всего две - эту и Шаблоны тестирования XUnit. Must read. Некоторые моменты можно обсуждать, но откровенных косяков или воды не нашёл. Что запомнилось.
1) разделение кода на четыре группы: бизнес-логика, контролёр в широком смысле этого слова как код, обрабатывающий входящие запросы, тривиальный код и переусложненный код, в котором есть и внешние зависимости и бизнес-логика. От последнего нужно избавляться через рефакторинг, сводя его к бизнес-логике или контроллерам. На бизнес-логику пишем модульные тесты, на контроллеры - интеграционные. На тривиальный код ничего не пишем)
2) не нужно писать отдельные тесты на каждой класс бизнес-логики, нужно тестировать процессы и стремиться к максимальному покрытию. Для бизнес-логики
3) использование минимум mock в модульных тестах, в идеальном случае их вообще не должно быть. Т.е идея в том, что классы бизнес-логики получают и возвращают DTO, которые можно создать в тесте без всяких mock
4) в случае интеграционных тестов mock нужно делать на неконтролируемые нами внешние зависимости, типа очередей, внешних АС, email транспорта. БД в случае если это не наша частная БД. Если же у нас микросервис со своей БД, которая обновляется вместе с приложением и внешние клиенты в нее не ходят - ее mock-ать не нужно, более того и заменять ее HBase тоже не нужно, т.к иначе мы не сможем полноценно оттестировать работу с БД, да и возникнут накладные расходы на поддержание двух версий скрипов. Это как раз то, где можно дискутировать, но милая мне нравится.
5) чем меньше в приложении слоёв, тем лучше, проще тестировать
6) логирование может являться контрактом, например по требованию сопровождения или если на его основе работает мониторинг, тогда его тоже нужно mock-ать и тестировать.
7) организовать отправку событий, например, логирования, сообщений в Kafka или SMTP можно через стандартизацию механизма событий в доменной модели и обработку событий в слое «контроллеров».
8) если вернутся к модели тестов AAA - Arrange, Act, Assert, то Arrange и Assert в одном тесте может быть много, а вот Act должен быть один, иначе у нас проблемы с нашим Java API. Исключение - интеграционные тесты где сложно поднять контекст.
9) методы с инициализацией тестов типа @Before плохи тем, что сложно читать текст, т.к часть Assert в другом методе. Поэтому лучше использовать отдельные говорящие private/protected методы с параметрами, чтобы можно было настроить тестовые данные под каждый тест
10) название тестового метода должно читаться как фраза, разделять можно подчёркиваниями в случае Java, фраза не обязательно должна быть шаблонной как в BDD, краткость более важна. Кстати, в Kotlin слова в названии теста можно разделять пробелами.
11) интеграционные тесты дают хорошее покрытие, за счёт этого увеличивают защиту от ошибок в коде. Поэтому их лучше всего писать на позитивные сценарии, покрывающие максимум кода и те негативные сценарии, которые не удалось покрыть юнит тестами
12) антихрупкость тестов, о которой я уже писал, становится особенно важна по мере развития проекта. Т.е после некоторого времени более важно, чтобы тесты были антихрупкими, чем чтобы они находили все потенциальные ошибки. Т.к для последнего есть QA и приёмочные тесты
Я перечислил лишь то, что запомнилось, интересным идеей в книге намного больше. Интересных и практических. Повторюсь - must read!
#books #unittests
1) разделение кода на четыре группы: бизнес-логика, контролёр в широком смысле этого слова как код, обрабатывающий входящие запросы, тривиальный код и переусложненный код, в котором есть и внешние зависимости и бизнес-логика. От последнего нужно избавляться через рефакторинг, сводя его к бизнес-логике или контроллерам. На бизнес-логику пишем модульные тесты, на контроллеры - интеграционные. На тривиальный код ничего не пишем)
2) не нужно писать отдельные тесты на каждой класс бизнес-логики, нужно тестировать процессы и стремиться к максимальному покрытию. Для бизнес-логики
3) использование минимум mock в модульных тестах, в идеальном случае их вообще не должно быть. Т.е идея в том, что классы бизнес-логики получают и возвращают DTO, которые можно создать в тесте без всяких mock
4) в случае интеграционных тестов mock нужно делать на неконтролируемые нами внешние зависимости, типа очередей, внешних АС, email транспорта. БД в случае если это не наша частная БД. Если же у нас микросервис со своей БД, которая обновляется вместе с приложением и внешние клиенты в нее не ходят - ее mock-ать не нужно, более того и заменять ее HBase тоже не нужно, т.к иначе мы не сможем полноценно оттестировать работу с БД, да и возникнут накладные расходы на поддержание двух версий скрипов. Это как раз то, где можно дискутировать, но милая мне нравится.
5) чем меньше в приложении слоёв, тем лучше, проще тестировать
6) логирование может являться контрактом, например по требованию сопровождения или если на его основе работает мониторинг, тогда его тоже нужно mock-ать и тестировать.
7) организовать отправку событий, например, логирования, сообщений в Kafka или SMTP можно через стандартизацию механизма событий в доменной модели и обработку событий в слое «контроллеров».
8) если вернутся к модели тестов AAA - Arrange, Act, Assert, то Arrange и Assert в одном тесте может быть много, а вот Act должен быть один, иначе у нас проблемы с нашим Java API. Исключение - интеграционные тесты где сложно поднять контекст.
9) методы с инициализацией тестов типа @Before плохи тем, что сложно читать текст, т.к часть Assert в другом методе. Поэтому лучше использовать отдельные говорящие private/protected методы с параметрами, чтобы можно было настроить тестовые данные под каждый тест
10) название тестового метода должно читаться как фраза, разделять можно подчёркиваниями в случае Java, фраза не обязательно должна быть шаблонной как в BDD, краткость более важна. Кстати, в Kotlin слова в названии теста можно разделять пробелами.
11) интеграционные тесты дают хорошее покрытие, за счёт этого увеличивают защиту от ошибок в коде. Поэтому их лучше всего писать на позитивные сценарии, покрывающие максимум кода и те негативные сценарии, которые не удалось покрыть юнит тестами
12) антихрупкость тестов, о которой я уже писал, становится особенно важна по мере развития проекта. Т.е после некоторого времени более важно, чтобы тесты были антихрупкими, чем чтобы они находили все потенциальные ошибки. Т.к для последнего есть QA и приёмочные тесты
Я перечислил лишь то, что запомнилось, интересным идеей в книге намного больше. Интересных и практических. Повторюсь - must read!
#books #unittests
Хабр
Книга «Принципы юнит-тестирования»
Привет, Хаброжители! Юнит-тестирование — это процесс проверки отдельных модулей программы на корректность работы. Правильный подход к тестированию позволит макс...
Всем привет!
Не JUnit-ом единым...
Если говорить о фреймворках для unit тестирования все наверняка вспомнят JUnit и Mockito. Но есть ещё много чего полезного в этой области. Сегодня расскажу про библиотеки для улучшения читаемости проверок - assert. Про важность читаемости кода, в т.ч тестового я уже писал.
Пример для затравки из AssertJ
assertThat(testList).hasSize(2) .containsExactly("index3", "index4") .isUnmodifiable();
Больше примеров см по ссылкам в конце поста.
Библиотек три.
1) Hamcrest. Старичок, родоначальник жанра, уже не развивается, не рекомендуется к использованию
2) AssertJ - в отличие от hamcrest построен на принципе method chaining, что позволяет использовать автопополнение IDE и выглядит более читаемо. Выводит более понятное сообщение об ошибке, что тоже важно. Есть фича Soft Assertion, позволяющая лениво описать n проверок и выполнить их за раз.
3) Truth - очень похож по принципу работы - method chaining - на AssertJ, при этом менее известен. В качестве преимущества его разработчики указывают более компактное API и более понятное логирование ошибок.
Как AssertJ, так и Truth позволяют создавать свои проверки.
За деталями предлагаю пойти сюда:
https://dzone.com/articles/hamcrest-vs-assertj-assertion-frameworks-which-one
https://habr.com/ru/post/675778/
https://truth.dev/comparison.html
#unittests #rare_test_libs
Не JUnit-ом единым...
Если говорить о фреймворках для unit тестирования все наверняка вспомнят JUnit и Mockito. Но есть ещё много чего полезного в этой области. Сегодня расскажу про библиотеки для улучшения читаемости проверок - assert. Про важность читаемости кода, в т.ч тестового я уже писал.
Пример для затравки из AssertJ
assertThat(testList).hasSize(2) .containsExactly("index3", "index4") .isUnmodifiable();
Больше примеров см по ссылкам в конце поста.
Библиотек три.
1) Hamcrest. Старичок, родоначальник жанра, уже не развивается, не рекомендуется к использованию
2) AssertJ - в отличие от hamcrest построен на принципе method chaining, что позволяет использовать автопополнение IDE и выглядит более читаемо. Выводит более понятное сообщение об ошибке, что тоже важно. Есть фича Soft Assertion, позволяющая лениво описать n проверок и выполнить их за раз.
3) Truth - очень похож по принципу работы - method chaining - на AssertJ, при этом менее известен. В качестве преимущества его разработчики указывают более компактное API и более понятное логирование ошибок.
Как AssertJ, так и Truth позволяют создавать свои проверки.
За деталями предлагаю пойти сюда:
https://dzone.com/articles/hamcrest-vs-assertj-assertion-frameworks-which-one
https://habr.com/ru/post/675778/
https://truth.dev/comparison.html
#unittests #rare_test_libs
DZone
Hamcrest vs. AssertJ
Let's compare two popular open Java assertion frameworks, Hamcrest and AssertJ, for testing RESTful APIs. Check out their features, syntax, and other perks.
Всем привет!
Давно хотел написать про паттерны/шаблоны программирования. Основной вопрос, возникающий при разговоре про паттерны - какая от них польза? Ведь главное - умеет человек кодить или нет.
С одной стороны паттерны - это лишь часть арсенала программиста. Можно заучить все паттерны, но не научиться кодить.
И тут возникает второй вопрос - о каких паттернах мы говорим?
1) самые известные - паттерны проектирования из книги «банды четырёх» https://refactoring.guru/ru/design-patterns/catalog
Это синглтон, фабричный метод, билдер и все все все
2) паттерны Enterprise архитектуры от Фаулера https://martinfowler.com/eaaCatalog/
3) паттерны рефакторинга https://refactoring.com/catalog/ Про них также говорится в книге Идеальная работа Мартина
4) паттерны модульных тестов http://xunitpatterns.com/ и снова в книге Идеальная работа
5) паттерны интеграции корпоративных приложений https://www.enterpriseintegrationpatterns.com/patterns/messaging/toc.html многие из которых можно встретить в стандарте JMS
6) паттерны микросервисных приложений https://microservices.io/patterns/index.html
7) даже у Kubernates есть паттерны https://www.redhat.com/cms/managed-files/cm-oreilly-kubernetes-patterns-ebook-f19824-201910-en.pdf
8) не говоря уже про антипаттерны https://javarush.ru/groups/posts/2622-chto-takoe-antipatternih-razbiraem-primerih-chastjh-1
9) 10) ...
Из этого списка можно сделать вывод, что паттерны могут быть везде. А из этого второй вывод: паттерны - это удобный способ описания какой-то области разработки. Собственно это и есть их ценность. Шаблоны помогают изучить новую технологию, читать статьи, книги и главное читать код и тесты. Ну и проектировать систему, обсуждать ее архитектуру с коллегами. По сути паттерны - это язык проектирования. А идеальный способ их использования - когда они уже реализованы в неком фреймворке: Singleton и MVC в Spring, Builder в Lombok, Sidecar в k8s, или в языке как Singleton и Decorator в Kotlin.
#patterns #refactoring #unittests
Давно хотел написать про паттерны/шаблоны программирования. Основной вопрос, возникающий при разговоре про паттерны - какая от них польза? Ведь главное - умеет человек кодить или нет.
С одной стороны паттерны - это лишь часть арсенала программиста. Можно заучить все паттерны, но не научиться кодить.
И тут возникает второй вопрос - о каких паттернах мы говорим?
1) самые известные - паттерны проектирования из книги «банды четырёх» https://refactoring.guru/ru/design-patterns/catalog
Это синглтон, фабричный метод, билдер и все все все
2) паттерны Enterprise архитектуры от Фаулера https://martinfowler.com/eaaCatalog/
3) паттерны рефакторинга https://refactoring.com/catalog/ Про них также говорится в книге Идеальная работа Мартина
4) паттерны модульных тестов http://xunitpatterns.com/ и снова в книге Идеальная работа
5) паттерны интеграции корпоративных приложений https://www.enterpriseintegrationpatterns.com/patterns/messaging/toc.html многие из которых можно встретить в стандарте JMS
6) паттерны микросервисных приложений https://microservices.io/patterns/index.html
7) даже у Kubernates есть паттерны https://www.redhat.com/cms/managed-files/cm-oreilly-kubernetes-patterns-ebook-f19824-201910-en.pdf
8) не говоря уже про антипаттерны https://javarush.ru/groups/posts/2622-chto-takoe-antipatternih-razbiraem-primerih-chastjh-1
9) 10) ...
Из этого списка можно сделать вывод, что паттерны могут быть везде. А из этого второй вывод: паттерны - это удобный способ описания какой-то области разработки. Собственно это и есть их ценность. Шаблоны помогают изучить новую технологию, читать статьи, книги и главное читать код и тесты. Ну и проектировать систему, обсуждать ее архитектуру с коллегами. По сути паттерны - это язык проектирования. А идеальный способ их использования - когда они уже реализованы в неком фреймворке: Singleton и MVC в Spring, Builder в Lombok, Sidecar в k8s, или в языке как Singleton и Decorator в Kotlin.
#patterns #refactoring #unittests
refactoring.guru
Каталог паттернов проектирования
Список паттернов проектирования, сгруппированый по предназанчению, сложности и популярности паттернов. В каталог включены объектно ориентированные паттерны, а также некоторые архитектурные паттерны.
Всем привет!
Сегодня рассмотрим циклические зависимости. Начнем с уровня классов и дойдем до слоев приложения.
1) классы. Технически Java компилятор умеет компилировать взаимозависимые классы за счет того, что является многопроходным - https://ru.wikipedia.org/wiki/Многопроходный_компилятор
Какие могут быть проблемы?
Главная - ухудшается читаемость кода. Да, я снова про нее) Вначале мы читаем метод класса А, затем переходим в зависимый класс Б, потом снова в А - в общем логику кода понять сложно. И это я привел достаточно простой пример.
Соответственно такой код сложнее рефакторить, в примере выше изменение public Java API класса А или Б приведет к изменению и второго класса.
Еще минус - невозможно использовать Dependency Injection через конструктор, а это наиболее естественный путь.
2) пакеты и модули. Проблема с вынесением общего кода. Если пакет\модуль содержит какую-то общую логику и при этом ни от чего не зависит - вынести и переиспользовать его в других проектах элементарно.
К тому же если за направлением зависимостей не следить - со временем общая логика начнет зависеть от прикладной и возникнет цикл. Об том, что это плохо говорит буква D из SOLID - абстракция не должна зависеть от деталей.
3) слои приложения. Число слоев может быть разным. В гексагональной архитектуре их по сути два - бизнес-логика и слой сервисов: веб-контроллеры, доступ к БД, вызовы внешних сервисов... https://lumpov.blogspot.com/2021/01/hexagonal-architecture.html В MVC - view, controller и model. Еще распространен такой вариант - presentation, services, model, storage. Но если образуется цикл, то вся система рушится как карточный домик. Слои имеют смысл, когда можно проследить направление запросов и зависимостей. А цель их применения: разделение бизнес-логики и инфраструктурного кода, что дает возможность достаточно легко сменить\добавить storage или внешнее API. Плюс слои по разному тестируются.
#arch #solid
Сегодня рассмотрим циклические зависимости. Начнем с уровня классов и дойдем до слоев приложения.
1) классы. Технически Java компилятор умеет компилировать взаимозависимые классы за счет того, что является многопроходным - https://ru.wikipedia.org/wiki/Многопроходный_компилятор
Какие могут быть проблемы?
Главная - ухудшается читаемость кода. Да, я снова про нее) Вначале мы читаем метод класса А, затем переходим в зависимый класс Б, потом снова в А - в общем логику кода понять сложно. И это я привел достаточно простой пример.
Соответственно такой код сложнее рефакторить, в примере выше изменение public Java API класса А или Б приведет к изменению и второго класса.
Еще минус - невозможно использовать Dependency Injection через конструктор, а это наиболее естественный путь.
2) пакеты и модули. Проблема с вынесением общего кода. Если пакет\модуль содержит какую-то общую логику и при этом ни от чего не зависит - вынести и переиспользовать его в других проектах элементарно.
К тому же если за направлением зависимостей не следить - со временем общая логика начнет зависеть от прикладной и возникнет цикл. Об том, что это плохо говорит буква D из SOLID - абстракция не должна зависеть от деталей.
3) слои приложения. Число слоев может быть разным. В гексагональной архитектуре их по сути два - бизнес-логика и слой сервисов: веб-контроллеры, доступ к БД, вызовы внешних сервисов... https://lumpov.blogspot.com/2021/01/hexagonal-architecture.html В MVC - view, controller и model. Еще распространен такой вариант - presentation, services, model, storage. Но если образуется цикл, то вся система рушится как карточный домик. Слои имеют смысл, когда можно проследить направление запросов и зависимостей. А цель их применения: разделение бизнес-логики и инфраструктурного кода, что дает возможность достаточно легко сменить\добавить storage или внешнее API. Плюс слои по разному тестируются.
#arch #solid
Wikipedia
Многопроходный компилятор
Многопроходный компилятор (англ. Multi-pass compiler) — тип компилятора, который обрабатывает исходный код или абстрактное синтаксическое дерево программы несколько раз (в отличие от однопроходного компилятора, который проходит программу только один раз).…
Всем привет!
Сегодня расскажу про такую штуку, как rate limiters.
Как следует из названия это компонент для ограничения числа запросов в интервал времени.
Может возникнуть вопрос - причем здесь Java? Да, обычно такие штуки разворачивают на инфраструктуре - например, в k8s или nginx.
Если так можно сделать - так и нужно делать)
Когда так сделать не получится:
1) алгоритм ограничения числа запросов нестандартный, поэтому нужна Java, чтобы его запрограммировать)
2) нужна возможность продвинутого мониторинга числа пропущенных и отбитых запросов
3) при изменении параметров rate limiting нужна умная ребалансировка. Т.е. нельзя просто сбросить счетчики в ноль, т.к. это приведет к падению сервиса, который защищает rate limiter.
4) ну и наконец нет подходящей инфраструктуры
Второй вопрос - в каких кейсах это может понадобиться:
1) не уронить стоящий за вами сервис
2) приоритизировать какие-то запросы вашего API по сравнению с другими чтобы не упасть самому
3) ограничить число запросов по тарифному плану клиента
Вот неплохая статья про существующие алгоритмы rate limiting https://habr.com/ru/post/448438
Неплохая библиотека для Java - Bucket4J https://github.com/bucket4j/bucket4j
Развивается порядка 8 лет, к слову разработчики из России.
Вот хорошее видео от разработчиков с примерам настройки - https://www.youtube.com/watch?v=OSNFNxgZZ3A
В простых случаях можно использовать Resilience4j, удобно со Spring Boot посредством аннотаций https://www.baeldung.com/spring-boot-resilience4j Resilience4j библиотека более широкого профиля, отвечает за отказоустойчивость, см. детали в статье
#java #libraries #highload #interview_question #rate_limiters
Сегодня расскажу про такую штуку, как rate limiters.
Как следует из названия это компонент для ограничения числа запросов в интервал времени.
Может возникнуть вопрос - причем здесь Java? Да, обычно такие штуки разворачивают на инфраструктуре - например, в k8s или nginx.
Если так можно сделать - так и нужно делать)
Когда так сделать не получится:
1) алгоритм ограничения числа запросов нестандартный, поэтому нужна Java, чтобы его запрограммировать)
2) нужна возможность продвинутого мониторинга числа пропущенных и отбитых запросов
3) при изменении параметров rate limiting нужна умная ребалансировка. Т.е. нельзя просто сбросить счетчики в ноль, т.к. это приведет к падению сервиса, который защищает rate limiter.
4) ну и наконец нет подходящей инфраструктуры
Второй вопрос - в каких кейсах это может понадобиться:
1) не уронить стоящий за вами сервис
2) приоритизировать какие-то запросы вашего API по сравнению с другими чтобы не упасть самому
3) ограничить число запросов по тарифному плану клиента
Вот неплохая статья про существующие алгоритмы rate limiting https://habr.com/ru/post/448438
Неплохая библиотека для Java - Bucket4J https://github.com/bucket4j/bucket4j
Развивается порядка 8 лет, к слову разработчики из России.
Вот хорошее видео от разработчиков с примерам настройки - https://www.youtube.com/watch?v=OSNFNxgZZ3A
В простых случаях можно использовать Resilience4j, удобно со Spring Boot посредством аннотаций https://www.baeldung.com/spring-boot-resilience4j Resilience4j библиотека более широкого профиля, отвечает за отказоустойчивость, см. детали в статье
#java #libraries #highload #interview_question #rate_limiters
Хабр
Ограничение скорости обработки запросов, или как не устроить DDoS-атаку на своего клиента
Иногда при разработке highload-продукта возникает ситуация, когда надо обработать не максимально большое количество запросов, а наоборот — ограничить количество запросов в единицу времени. В нашем...
Всем привет!
Поговорим про микросервисы. Статей будет несколько, от плюсов и минусов до саги и прочих паттернов.
Для начала что такое микросервис. Я дам определение в виде набора обязательных признаков. Обязательны все из них.
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.