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

Небольшое развитие поста про выбор альтернативы IDEA Ultimate.
В посте особое внимание по понятным причинам было обращено на поддержку Spring.
Так вот, благодаря комментариям выяснилось, что в России пилится 3 плагина для Spring:

1) Amplicode https://amplicode.ru/
2) Sber\Giga - назовем его так, т.к. его еще никто не видел
3) Explyt https://github.com/explyt/spring-plugin/wiki/Videos

Последний я изучил по видосам - выглядит неплохо как плагин чисто для Spring. А Amplicode вырывается вперед благодаря поддержке JPA, Liquibase, MapStruct, Docker....

Конкуренция, однако. Вот что импортозамещение делает) Будем наблюдать.

За наводку на Explyt спасибо @alex_inozemtsev

#idea #spring
November 8, 2024
Всем привет!

Небольшая памятка по созданию бинов в Spring контексте. Рассматриваем Spring Boot приложение.
Что влияет на итоговый набор бинов?

0) база - влияют собственно определенные в сервисе бины.

Я насчитал 5 способов определения бинов в порядке распространнености:
а) Spring annotation-based - через @Component сотоварищи
б) Java-based configuration, причем что интересно - методы @Bean могут быть не только у них, но и у Annotation-based, о разнице можно почитать тут: https://stackoverflow.com/questions/3330618/bean-inside-class-with-configuration-and-without-it
в) старые и уже наверное не добрые xml-based configuration
г) экзотические groovy bean definitions https://docs.spring.io/spring-framework/reference/core/beans/basics.html#beans-factory-groovy
д) Spring понимает JSR 330 (привет от Java EE) аннотации при добавлении соответствующих зависимостей https://docs.spring.io/spring-framework/reference/core/beans/standard-annotations.html

Причем способы можно миксовать, например, добавляя xml и groovy конфигурации через context.loadBeanDefinitions() или @ImportResource.

1) кроме того бины тянутся из подключенных в проекте стартеров, при включенной @AutoConfiguration (или @SpringBootApplication), исключить лишнее можно через параметр exclude

2) корневые пакеты для поиска бинов. По умолчанию он один и равен пакету, где лежит класс с @SpringBootApplication. Может быть переопределен @SpringBootApplication(scanBasePackages = "com.example.myproject"). К слову, @SpringBootApplication под капотом включает в себя @ComponentScan - автоматический поиск бинов.

3) можно не использовать автоматический поиск бинов (@ComponentScan), а собрать их в конфигурации и явно их импортировать через @Import

4) у Spring Data JPA свои настройки корневых пакетов для поиска репозиториев, указываются через @EnableJpaRepositories(basePackages="com.example.myproject")

5) использование профилей Spring при запуске и наличие @Profile в @Configuration и @Component

6) более гибко условное подключение бинов можно сделать через разного рода @Conditional. Это целый пакет аннотаций, бывают условия с SpeL и даже бины, задающие условие c помощью Java кода. Детальнее тут https://www.baeldung.com/spring-boot-annotations

7) можно вклинится в момент, когда метаданные (BeanDefinition) уже созданы, а Bean - еще нет, через создание своего BeanFactoryPostProcessor https://docs.spring.io/spring-framework/reference/core/beans/factory-extension.html#beans-factory-extension-factory-postprocessors
и что-нибудь подшаманить - например, заменить bean.

8) есть печально знаменитая опция allowBeanDefinitionOverriding, позволяющая переопределять бины просто создавая новый бин с тем же интерфейсом позже по времени

9) более предсказуемая, но также не рекомендуемая аннотация @Primary на компоненте, "хардкодящая" главный бин для внедрения

И возможно, я что-то забыл)

Вот такой простой и понятный процесс инициализации бинов. Казалось бы, что может пойти не так?)

Например, это приводит к головной боли у разработчиков плагинов, пытающихся воссоздать Spring context без запуска Spring приложения. Это не так-то легко в первую очередь из-за динамических частей - там где наличие бина определяется в Java коде. Вот хорошая, "с кишочками" статья про решение этой проблемы https://habr.com/ru/companies/explyt/articles/854304/
И выходит, что создатели Spring, увы, не подумали о разработчиках плагинов.

#spring #ide
November 12, 2024
Всем привет!

Последняя статья из цикла про журналирование.

Все разработчики сталкиваются с разбором логов. При отладке, в логах сервиса на тестовых средах и ПРОМ, в логах пайплайна сборки или деплоя... Но не все там находят причину проблемы. Перечислю типовые ошибки при разборе. К слову - со всеми из них я сталкивался на практике. Поехали!

1) копать глубже. Найти первую попавшуюся ошибку в логе (или последнюю, смотря с какого края просматривается лог) и успокоится - плохо. Пусть разработчики сервиса, который пишет эти логи, придерживаются правила: ERROR - это всегда ERROR. Все равно - ошибок может быть несколько, ошибки могут выстраиваться в цепочку. Вывод - нужно анализировать весь(все) лог(и).

2) первопричина. Окей - смотрим весь лог, видим цепочку идущих подряд ошибок. Куда смотреть? На первую ошибку по времени. И последнюю в стектрейсе. Это и есть первопричина.

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

4) случайные ошибки. Если анализировать достаточно большой лог - там с большой вероятностью будут случайные ошибки. "Моргнула" сеть, рестартовал какой-то сервис... Особенно в тестовом контуре, но и на ПРОМе тоже. Снова вопрос - как понять, что ошибку не стоит разбирать? Первое - сравнить количество ошибок разных типов, второе - снова насмотренность.

5) не те логи. В k8s есть логи контейнера сервиса, а есть логи его сайдкаров. Если брать Openshift - для раскатки новой версии DeploymentConfig сервиса в нем используются т.наз. deploy поды, их логи как правило бесполезны. Если брать сервера приложений - там есть логи собственно сервера, а есть - каждого сервиса, развернутого на нем. Ошибка в логах джобы деплоя часто даёт мало информации о причине падения деплоя, нужно смотреть логи контейнеров. Т.е. вначале нужно определится - где может быть ошибка, которую мы ищем. И в конце концов - если в ваших логах есть ошибка интеграции, но причина ее не понятна - стоит попробовать достать логи смежного сервиса.

6) чужая ошибка. Если у нас нагруженная многопользовательская система - в логах могут встречаться ошибки разных пользователей. А мы сейчас разбираем проблему конкретного. Тут поможет фильтрация по атрибуту логов, содержащего id пользователя. Если конечно такой атрибут есть) Спойлер: должен быть)

7) ошибки не то, кем кажутся. Самый сложный кейс - когда сообщение об ошибке не дает полезной информации и даже вводит в заблуждение. Четкого рецепта тут нет - поисковик, ChatGPT, помощь более опытных коллег, просмотр по логам действий, предшествующих ошибке...

#logging
November 23, 2024
Всем привет!

На этот раз у поста будет заголовок.

7 смертных грехов платформенных компонент.

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

Поехали!

1) слишком тесная связанность. Внешний компонент можно добавить в свой сервис разными способами. Как отдельный под рядом с основным сервисом, как сайдкар, как jar библиотеку. Варианты расположены в порядке увеличения связанности. Больше связанности - это плохо. Почему - отвечу на примере подключаемого jar. Как известно jar не приходит один. А тянет за собой зависимости. Их может быть много, они могут конфликтовать с другими зависимостями, содержать уязвимости, замедлять процесс старта, конфликтовать за общие ресурсы. Процесс подключения может быть нетривиален. А если подключение идет через корневой pom, то мы в каком-то смысле передаем управление нашим сервисом наружу.

2) отдельный пайплайн. Беда не приходит одна, и бывает так что внешний компонент тянет за собой свой CI или CD пайплайн. И то, и другое для конкретного сервиса может быть достаточно сложной и месяцами тщательно настраиваемой штукой. А теперь нам надо встроить туда чужой пайп. Или вызовом, или разобраться что же такого сложного делает внешний пайп и сэмулировать это в своем (а-ля реверс-инжиниринг). Второй вариант - сложно, остановимся на первом. Какие могут быть сложности с внешним пайпом?
а) много непонятных настроек
б) управляет версионированием дистрибутива (снова тесная связанность)
в) делает commits в наш репозиторий
г) требует особых доступов
д) замедляет сборки\установку
е) не дает настраивать манифесты k8s

3) плохая документация. Какой минимум там должен быть:
а) процесс подключения
б) все (!) настройки
в) типовые ошибки и способы их устранения
г) минимальные требования по ресурсам и их изменение в зависимости от нагрузки
д) контакты поддержки
е) канал с информацией об обновлениях

С чем я сталкивался:
а) неочевидные проблемы при сборке\установке. Когда из сообщения об ошибке и документации вообще не ясна причина проблемы
б) неожиданное поведение в runtime, связанное с повышенным потреблением памяти, не описанное четко в документации
в) нет информации о том, в какой версии поддерживается конкретная фича (реализовано требование)
г) нет инструкции по обновлению на новую версию. При этом есть опасение, что поднятием версии процесс не ограничивается
д) магия и ее отсутствие. Поясню. Обещали, что все заработает само. Оно не работает. Почему - не ясно, т.к. принцип работы не описан.

4) имитация поддержки. Она как правило всегда есть, но иногда по факту ее нет) Вот такая диалектика) Хорошо, когда компонента работает из коробки. Как рассказывали на митапе, где она была презентована. А если нет?)
Что я ожидаю:
а) чатик\почту от разработчиков компоненты. лучше чатик
б) таск-трекер и SLA по обработке задач
в) канал с новостями
г) отдельная команда, помогающая с подключением (при необходимости)
д) чатик пользователей для обмена опытом

5) повышенное потребление ресурсов. Компонент закрывает какое нефункциональное требование к системе. Таких требований много. А еще есть бизнес-логика. И выглядит странно, когда один служебный компонент в минимально рекомендуемой конфигурации "жрёт" столько же или больше ресурсов, чем собственно бизнес-сервис.

6) не держит нагрузку. От платформенного "велосипеда" я ожидаю оптимизации в плане производительности. Т.е. он должен держать любую нагрузку, которая может быть в компании, где эта платформа разработана. Не требуя при этом слишком много железа. Иначе зачем это все?)
November 29, 2024
7) слишком сложный релизный цикл. Это когда подключение компонента требует пройти 7 кругов ада. А прохождения некоторых кругов приходится ждать неделями или месяцами, например, из-за отсутствия железа. Я ожидаю понятного чек-листа подключения по всем средам - дев, тест и пром - и плана внедрения учитывающего наличие железа. Особенно если компонент подключается массово, или он обязательный.


P.S. Если говорить в позитивном ключе - что должны быть у переиспользуемой компоненты - см. мой пост https://t.me/javaKotlinDevOps/162

#platform
November 29, 2024
Всем привет!

Я сейчас на Highload-е. Оффлайн.
Сделаю серию заметок по докладам, которые посетил. Названия докладов примерные)

Нужен ли кэш при работе с современными БД?
Казалось бы ответ очевиден, но нет) В смысле не всегда.
Если брать инстанс СУБД Postgres или MySQL - да, кэш будет быстрее, но не то, что на порядок - даже не в разы. Два основных условия - инстанс БД используется только для чтения и используется актуальная версия СУБД. Более того - они ещё и неплохо масштабируются по ядрам CPU. Вопрос что дешевле - держать сервер кэша или ещё один сервер СУБД. В общем эффективность кэша нужно подтверждать на НТ.

Еще одно открытие для меня - MySQL не сильно медленнее Postgres. А пропатченный - даже быстрее. У меня был травматический опыт десятилетней давности с MySQL и обратное представление)

P.S. Если кто ещё на конференции?

#rdbms #caching
December 2, 2024
December 2, 2024
Транзакционность в Kafka.

Транзакцию легко реализовать в рамках БД. Еще есть распреденные транзакции: старый недобрый JTA - долго и дорого, паттерн Сага с eventually consistentcy - работает, при этом требует проработки архитектуры системы в целом.

А что Kafka?
Казалось бы - append only запись и чтение, какие транзакции? Транзакции внутри Kafka - запись на N брокеров, Zookeeper и возврат ответа в producer - выносим за скобки, не было бы тут транзакционности - кто бы ей пользовался)

Но транзакции могут быть на уровне бизнес-логики. Например, мы можем перекладывать сообщения из одного топика в другой попутно выполняя над ними преобразования. Запись - понятно, а чтение из Kafka - это тоже запись, точнее сдвиг текущей позиции для данного consumer в топике.
Так что с транзакциям внутри Kafka (внутри- это принципиально)? Они есть. С ACID, все как положено.
Детали тут https://www.confluent.io/blog/transactions-apache-kafka/
Интересно, что запись в топике появится сразу. Но это запись «почтальона Печкина», consumer вам ее не покажет, потому что у вас документов нету, тьфу, не то, потому что транзакция не зафиксирована) Регулируется это служебными заголовками. Данный лайфхак улучшает время чтения данных из транзакции, по сути это предварительное кэширование.

Полноценно функционал доступен начиная с версии 3.6. Последняя на данный момент 3.9

#kafka #transactions #streaming
December 2, 2024
December 3, 2024
Как мне напомнили в комментариях - история развивается по спирали.
Очереди (очереди не равно топики Kafka) есть в Oracle https://docs.oracle.com/en/database/oracle/oracle-database/19/adque/aq-introduction.html
И они поддерживают общие транзакции с таблицами.
Также у Oracle есть шлюз для связывания внутренних и внешних очередей https://docs.oracle.com/en/database/oracle/oracle-database/21/adque/messaging_gateway.html

Аналогично для MSSQL https://learn.microsoft.com/ru-ru/sql/database-engine/service-broker/benefits-of-programming-with-service-broker?view=sql-server-ver16

Спасибо @AViIgnatov

#rdbms #mq #transactions
December 3, 2024
Ещё один интересный доклад - про самую производительную утилиту для НТ - и это wrk2. Позволяет на типовой машине 8 ядер и 12 Гб (надеюсь правильно запомнил конфигурацию, но плюс минус так) подымать 10 000+ параллельных соединений в несколько потоков. Плюс минимизировать эффект от временной недоступности тестируемой системы - т.наз. Coordinated Omission - как числом потоков, так и математической корректировкой результата теста и дальнейшей последовательности вызовов. Надеюсь, заинтриговал) Минус - утилита заброшена автором с 2016 года, но этот минус поправили, см https://t.me/rybakalexey/98
По ссылке - блог автора доклада. Плюс сам доклад https://t.me/rybakalexey/170

#нт #tools
December 3, 2024
Ещё интересный доклад - про нетривиальное использование трейсинга (tracing).

Начнём с того, что далеко не у всех он настроен. Окей, настроили. Теперь нам проще разбирать проблемы на ПРОМ в микросервисной архитектуре.

Все?

Нет) А если взять трейсы за некий период, отсемплировать их уменьшив объём и сложить их в графовую БД? Получим реверс-инжиниринг архитектуры. Причём это будет не «мертвая» архитектура, по кем-то когда-то написанной аналитике, а настоящая. Да, не 100% точная, из-за мертвых интеграций и поломанного трейсинга, но все же. Что с ней можно сделать? Контролировать... архитектуру. Например:
- Общая схема вызовов
- Циклические ссылки
- Длина цепочек вызовов
- Лишние с точки зрения разделения на бизнес-домены связи
- Критичность сервисов - сервисы, которые чаще всего используются другими сервисами
- Однотипные вызовы, которые можно объединить в batch запрос
- Вызовы в цикле
- Анализ использования практики Graceful degradation

Сама идея - практически бесплатный для бизнес-команд инструмент анализа архитектуры - отлично IMHO.

P.S. для этих же целей в теории можно использовать данные из Service Mesh, только добавляется ещё один снижающий точность фактор - не все компоненты находятся в облаке (и не все компоненты в облаке используют Service Mesh)

P.P.S. Конечно идея идеально подходит для компаний а-ля Яндекс и Авито (собственно в Авито ее и внедрили) - там, где нет жёсткого контроля интеграций на уровне согласования аналитики. Но IMHO в любом случае можно использовать как контрольный механизм, ещё одну «сеть»

#arch #tracing
December 3, 2024
December 4, 2024
December 6, 2024
Всем привет!

Уже писал про импортозамещение в ПО:

1) IDE https://t.me/javaKotlinDevOps/353
2) Spring plugins https://t.me/javaKotlinDevOps/358

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

Так вот, на Highload-е нашёл 2 аналога GitLab/GitHub:

1) GitVerse от Сбера. Как и GigaIDE находится в процессе разработки, в частности аналог Git Actions планируют допилить в ближайших релизах, сейчас для настройки требуется много ручных действий. Две основные фишки: интеграция с GigaCode и облачная IDE на основе VS Code, доступная в рамках бета-тестирования. Для облачной IDE нужен аккаунт и настройка в облаке Сбера - cloud.ru. И к сожалению не поддерживается Java - зато есть Python, Go, С#. Интерфейс аскетичный, но подсветка синтаксиса, AutoComplete, работа с тестами, отладка и консоль есть. Пока все бесплатно, конечно временно) Еще фишка - доступно AI ревью кода https://gitverse.ru/docs/collaborative-work/code-review/#ai-ревью И в целом ревью неплохое - я скормил ему кусок кода "с запахами", и AI нашел там 6 багов из 18. Минус: все работает медленно - думаю сказывается бесплатность и статус беты.

2) GitFlic от Астра (которая Linux). В нём есть Ci/CD, реестр артефактов, статический анализ кода (названный почему-то SAST), REST API и интеграция с Telegram и JIRA. И платная версия, позволяющая добавлять более 5 человек в private репозиторий плюс расширенную поддержку https://gitflic.ru/price Пока нет полноценно баг-трекера, обещают сделать. Еще плюс - тут я быстро нашел как разрешить force push, в отличие от GitVerse)

GitFlic на данный момент выглядит законченным продуктом, но у GitVerse есть свои плюсы - см. выше. Плюс тесная интеграция с облачным провайдером cloud.ru

#импортозамещение #git
December 10, 2024
December 14, 2024
Всем привет!

Читаю сейчас книжку Open Telemetry, первая глава, вступление о том, зачем все это нужно.
Обычно во вступлении не так много ценной информации, но бывают исключения)

Нашел сравнение Open Telemetry с DevOps. Казалось бы, что может быть у них общего? Кроме того, что DevOps инженеры настраивают Open Telemetry систему совместно с разработчиками и сопровождением.

Ответ - идеология.

Для начала - что такое DevOps?
Набор инструментов - да. Но в первую очередь идея о том, что для быстрого деплоя приложения на ПРОМ важно сотрудничество Dev и Ops, а также забытых в этой формуле Test и Sec)

Аналогично, OpenTelemetry - это не просто стандарт сбора, передачи и хранения логов, метрик и трейсов. Это идея о том, что для разбора, а в идеале предсказания проблемы нужно единое представление перечисленных ранее данных. Назовем все эти данные телеметрией.
Т.е. ключевая идея - сами по себе логи, метрики и трейсы конечно же полезны. Но вместе их ценность сильно возрастает. И, естественно, в телеметрии должны быть признаки для матчинга этих данных между собой.

Читаю дальше)

Ввожу новый тэг -
#telemetry
December 15, 2024
Всем привет!

Есть много способов получить данные из БД с помощью JPA:
1) JPQL
2) JPQL Native Query
3) HQL
4) Spring Data JPA Repository
5) Criteria API
6) QueryDSL
...

Предположим, нам нужно вернуть набор строк. Задать параметры запроса можно по-разному, а итог будет один - List или другая коллекция (Collection) с набором данных. Верно? Не совсем)
Если посмотреть на список возвращаемых Spring Data JPA данных https://docs.spring.io/spring-data/jpa/reference/repositories/query-return-types-reference.html#appendix.query.return.types то там можно увидеть много чего интересного. В т.ч. Stream.
А вот пример его использования: https://vladmihalcea.com/spring-data-jpa-stream/
Аналогично можно вернуть Stream и из обычного JPA - см. метод getResultStream, вот пример: https://thorben-janssen.com/jpa-2-2s-new-stream-method-and-how-you-should-not-use-it/

Зачем это может быть нужно?

Во-первых это просто красиво... Шучу. Если вы используете Stream в бизнес-логике - то кажется логичным использовать их и при обращении к БД.
А во-вторых: главная особенность стриминга - равномерная выборка данных. И в каждый момент данных в обработке будет одна запись.
Рассмотрим кейс, когда нужно обработать на клиенте миллион записей.

Ремарка: если у вас такой кейс - подумайте, нет ли проблем в архитектуре. Данные лучше обрабатывать на сервере СУБД. Если все же проблем нет - продолжим)

Так вот, какие у нас варианты:
1) вытащить на клиент миллион записей. Запрос к БД будет один, она выдержит, но с неплохой вероятностью можно убить клиент через Out of Memory.
2) организовать пагинацию, например, вот так: https://www.baeldung.com/spring-data-jpa-iterate-large-result-sets. Данных на клиенте в моменте не много, по размеру страницы, но запросов к БД ... тысяча.
3) использовать стримы. Запрос к БД один, данных на клиенте немного. Не обязательно одна запись, но в любом случае немного, детали ниже.

К слову, стриминг по БД с JPA аналогичен перемещению курсора по ResultSet в JDBC. С накладными расходами и плюшкам, которые дает сессия JPA, конечно.

И про объем данных на клиенте. Казалось бы - вытаскиваем записи поштучно. Но если не указать fetch size - объём предварительной выборки - для некоторых СУБД Hibernate вытащит на клиента все данные за раз, и мы вернемся к варианту 1 (((

#jpa #java_streams #rdbms
December 19, 2024
Всем привет!

Я снова вернулся)

И предновогодний пост будет снова про AI и Java.

Для начала про LLM. Чтобы LLM дала осмысленный ответ - ей нужен правильный промт и побольше контекста. Не даром в новых версиях моделей объем контекста растет - возьмем тот же Gemini с 1 млн токенов.
Но с точки зрения разработки - важен не только объем, но автоматизация работы с контекстом, т.е. некая бизнес-логики. Например, если мы делаем свой агент и у нас несколько источников точных данных, которые мы хотим скормить модели. И эта бизнес-логика скорее всего будет похожая у разных агентов...
LLM - область достаточно молодая, стандарты в ней зарождаются прямо сейчас. Встречайте MCP https://spec.modelcontextprotocol.io/specification/ - сайт стандарта - и https://habr.com/ru/articles/862312/ - интро на русском.
Он стандартизирует в первую очередь транспортное API - клиент и сервер - для работы с источниками точных данных и LLM. Содержит ряд готовых серверов для работы с файловыми данными, СУБД, веб-поиском.

Как это все относится к Java? А вот как: есть Spring AI, уже писал про него https://t.me/javaKotlinDevOps/241 Он дает универсальное API для обращения к различным LLM. Сейчас туда добавили - в статусе experimental - Spring AI MCP https://docs.spring.io/spring-ai/reference/api/model-context-protocol.html
Причем добавили достаточно быстро, хотя до Python конечно же далеко. Вообще поддержка Python, я полагаю, появилась вместе со стандрартом)

P.S. Да, вспоминая Kotlin в названии канала - если посмотреть примеры Spring AI - получите, распишитесь: https://github.com/spring-projects/spring-ai-examples/blob/main/kotlin/

#llm #spring
December 31, 2024