Всем привет! Поговорим про микросервисы и их преимущества. Статей будет несколько, от плюсов и минусов до саги и прочих паттернов. Для начала что такое микросервис. Я дам определение в виде набора обязательных признаков. Обязательны все из них. 1) одна команда 2) отдельная кодовая база 3) отдельная БД 4) отдельный pipeline 5) отдельный релизный цикл 6) небольшой объем кода 7) взаимодействие с другими сервисами через межпроцессное API. REST, gRPC, GraphQL, Kafka - это то, что сейчас на слуху, на самом деле технологий больше. Так какие же плюсы у микросервисов: 1) один сервис разрабатывает одна команда...
(java || kotlin) && devOps
Всем привет! Сегодня расскажу про такую штуку, как 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 - Backet4J 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
Всем привет! Сегодня рассмотрим циклические зависимости. Начнем с уровня классов и дойдем до слоев приложения. 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
Всем привет! Давно хотел написать про паттерны/шаблоны программирования. Основной вопрос, возникающий при разговоре про паттерны - какая от них польза? Ведь главное - умеет человек кодить или нет. С одной стороны паттерны - это лишь часть арсенала программиста. Можно заучить все паттерны, но не научиться кодить. И тут возникает второй вопрос - о каких паттернах мы говорим? 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
Всем привет! Не 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
Всем привет! Ещё одно ревью на книгу, которую упоминал: Владимир Хориков Принципы юнит-тестирования. 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
