Всем привет!
Увидел у коллеги аналитика в канале интересное мнение. Разработка - это синоним унижения, т.к. у вас всегда что-то не будет работать.
С мнением не согласен)
Посыл, что что-то всегда не будет работать - абсолютно верный. Причём сломаться может даже сборка проекта, в котором ещё ничего не меняли. Т.е. разработка ещё не началась, а уже что-то сломалось) Обидно)
Только на мой взгляд такая ситуация - это вызов. И в т.ч. и в этом кайф от разработки.
P.S. Но как я писал ранее в посте примере про внедрение проблемной технологии (крепостное слово Jixb): если решение вызова занимает неделю или две, вместо пары дней, и все это время команда ждёт - вы свернули не на ту дорогу. Не всякие вызовы стоит принимать)
#dev
Увидел у коллеги аналитика в канале интересное мнение. Разработка - это синоним унижения, т.к. у вас всегда что-то не будет работать.
С мнением не согласен)
Посыл, что что-то всегда не будет работать - абсолютно верный. Причём сломаться может даже сборка проекта, в котором ещё ничего не меняли. Т.е. разработка ещё не началась, а уже что-то сломалось) Обидно)
Только на мой взгляд такая ситуация - это вызов. И в т.ч. и в этом кайф от разработки.
P.S. Но как я писал ранее в посте примере про внедрение проблемной технологии (крепостное слово Jixb): если решение вызова занимает неделю или две, вместо пары дней, и все это время команда ждёт - вы свернули не на ту дорогу. Не всякие вызовы стоит принимать)
#dev
Всем привет!
Продолжим про тестовые библиотеки. Достаточно часто возникает задача проверить в тесте структурированные текстовые данные. Я про XML, json и yaml как самые распространённые варианты. XML первым указан по старшинству, а не распространённости) И он пока ещё жив.
Зачем для этого нужно специализированная библиотека:
1) текст может отличаться формированием - пробелами и переносами строк. Иногда это важно при сравнении, но часто нужно проигнорировать
2) могут быть «лишние» тэги/атрибуты - которые не должны участвовать в сравнении
3) может отличаться порядок тэгов
4) может стоят задача проверить только отдельные элементы в дереве, или их количество
Одной библиотеки для всех форматов нет, но есть две - XMLUnit и JsonAssert. Так думал я, пока не начал копать тему глубже. И искать, чем же можно проверить yaml. Оказывается, есть «более лучшая» замена JsonAssert, которая:
1) умеет и в json, и в yaml, причём может сравнивать json и yaml. И также сравнивать с Java обьектом
2) умеет все это делать в стиле Assert/Truth, он же method chaining. А это облегчает как написание условий проверки благодаря auto competition в IDE, так и их чтение. А возможно в некоторых случаях позволит отказаться от BDD фреймворка.
3) прозрачно работает с разными источниками данных - строка и файл
Встречайте - ModelAssert https://github.com/webcompere/model-assert
И более подробно https://www.baeldung.com/json-modelassert
Что интересно - автор сам написал статью на baeldung и ссылается на неё в документации.
Ещё важный момент - подчеркивается совместимость библиотеки со Spring MockMvc и Mockito. Возможно даже ради этой поддержки автор и запилил совместимость с Hamcrest. И последнее - отдельно продумано тестирование json с guid. Во-первых можно игнорировать различия конкретных значений uuid (они могут генерироваться в каждом тесте), во-вторых легко написать проверку формата uuid.
А что же XML - вот хорошая статья по XMLUnit https://www.baeldung.com/xmlunit2
Он может примерно то же самое, но без method chaining. Но зато с валидацией по схеме (xsd). Что кстати неплохо характеризует отличия в подходах к работе с XML и json)
#unittests #rare_test_libs #XML #json #yaml
Продолжим про тестовые библиотеки. Достаточно часто возникает задача проверить в тесте структурированные текстовые данные. Я про XML, json и yaml как самые распространённые варианты. XML первым указан по старшинству, а не распространённости) И он пока ещё жив.
Зачем для этого нужно специализированная библиотека:
1) текст может отличаться формированием - пробелами и переносами строк. Иногда это важно при сравнении, но часто нужно проигнорировать
2) могут быть «лишние» тэги/атрибуты - которые не должны участвовать в сравнении
3) может отличаться порядок тэгов
4) может стоят задача проверить только отдельные элементы в дереве, или их количество
Одной библиотеки для всех форматов нет, но есть две - XMLUnit и JsonAssert. Так думал я, пока не начал копать тему глубже. И искать, чем же можно проверить yaml. Оказывается, есть «более лучшая» замена JsonAssert, которая:
1) умеет и в json, и в yaml, причём может сравнивать json и yaml. И также сравнивать с Java обьектом
2) умеет все это делать в стиле Assert/Truth, он же method chaining. А это облегчает как написание условий проверки благодаря auto competition в IDE, так и их чтение. А возможно в некоторых случаях позволит отказаться от BDD фреймворка.
3) прозрачно работает с разными источниками данных - строка и файл
Встречайте - ModelAssert https://github.com/webcompere/model-assert
И более подробно https://www.baeldung.com/json-modelassert
Что интересно - автор сам написал статью на baeldung и ссылается на неё в документации.
Ещё важный момент - подчеркивается совместимость библиотеки со Spring MockMvc и Mockito. Возможно даже ради этой поддержки автор и запилил совместимость с Hamcrest. И последнее - отдельно продумано тестирование json с guid. Во-первых можно игнорировать различия конкретных значений uuid (они могут генерироваться в каждом тесте), во-вторых легко написать проверку формата uuid.
А что же XML - вот хорошая статья по XMLUnit https://www.baeldung.com/xmlunit2
Он может примерно то же самое, но без method chaining. Но зато с валидацией по схеме (xsd). Что кстати неплохо характеризует отличия в подходах к работе с XML и json)
#unittests #rare_test_libs #XML #json #yaml
GitHub
GitHub - webcompere/model-assert: Assertions for data models
Assertions for data models. Contribute to webcompere/model-assert development by creating an account on GitHub.
Всем привет!
Продолжение про тесты, на этот раз интеграционные.
Доминирующий протокол для интеграции сейчас - http(s). REST, GraphQL, разные кастомные реализации. Если мы получаем данные по http - значит где-то есть http client. Он куда-то стучится. Как можно протестировать эту часть интеграционной цепочки?
На первый взгляд, самый простой вариант - развернуть в TestContainers реальный экземпляр сервиса поставщика. Но у нас же микросервисы, а значит он, возможно, вызывает ещё кого-то. И т.д. Даже если не вызывает - ходит в БД.
Поэтому для теста, да и для отладки, нужна заглушка. Кстати, в большинстве случаев это может быть одна и та же заглушка.
Заглушка может быть standalone и встроенная. Я за встроенную, т.к. удобно держать в одном репозитории с кодом и сам код, и все необходимые настройки для его отладки и работы. Кроме стендозависмых конечно.
Что нас нужно от заглушки:
1) возможность старта на произвольном порту и передачи этого порта в код (сервер всегда localhost). Захардкодить порт можно, но тесты запускаются на CI конвейере, в т.ч. на используемом совместно сервере. И вообще не должны зависеть от среды запуска.
2) удобная возможность задавать разные ответы для разных path, например, method chaining
3) учёт параметров в URL
4) возможность задать http код ответа
5) возможность задать http заголовки ответа
6) возможность эмулировать таймаут
Ну и в идеале простая инициализация в тесте, например, с помощью аннотации.
И вот наконец кандидаты.
1) Wiremock. Главные плюсы - большое число возможностей, накопленных за время существования, и возможность работы как во встроенном режиме, так и standalone. Примеры фишек Wiremock - сценарный режим, когда можно задать ответ в зависимости от предыдущего запроса. Встроенный генератор случайных данных в полях ответа. Возможность использовать данные запроса в ответе (templating). Статья как подружить Wiremock с тестами https://www.baeldung.com/spring-webclient-wiremock-integration-testing
2) @RestClientTest - это «магическая» аннотация Spring Boot, автоматически связывающая добавленный в класс теста RestTemplate с сервером заглушки. Т.е. настройку порта делать не нужно. Вот статья по использованию https://www.baeldung.com/restclienttest-in-spring-boot
Если используете Spring и RestTemplate или его наследника RestClient - наверное оптимальный вариант. Для WebClient - асинхронщины и реактивщины - не годится, соответствующий тикет был отклонен командой Spring https://github.com/spring-projects/spring-boot/issues/8404
3) А рекомендует Spring для WebClient использовать MockWebServer. Вот статья https://howtodoinjava.com/java/library/mockwebserver-junit-webclient/
Из интересных фишек я заметил возможность получения информации о последнем запросе, пришедшем на сервер. И простейший сценарный режим, когда можно настроить очередь ответов для ряда последовательных запросов. Настраивать сложнее, чем предыдущие два варианта.
Итого: рекомендую @RestClientTest для синхронного Spring и Wiremock для остального.
#integration_tests #rare_test libs
Продолжение про тесты, на этот раз интеграционные.
Доминирующий протокол для интеграции сейчас - http(s). REST, GraphQL, разные кастомные реализации. Если мы получаем данные по http - значит где-то есть http client. Он куда-то стучится. Как можно протестировать эту часть интеграционной цепочки?
На первый взгляд, самый простой вариант - развернуть в TestContainers реальный экземпляр сервиса поставщика. Но у нас же микросервисы, а значит он, возможно, вызывает ещё кого-то. И т.д. Даже если не вызывает - ходит в БД.
Поэтому для теста, да и для отладки, нужна заглушка. Кстати, в большинстве случаев это может быть одна и та же заглушка.
Заглушка может быть standalone и встроенная. Я за встроенную, т.к. удобно держать в одном репозитории с кодом и сам код, и все необходимые настройки для его отладки и работы. Кроме стендозависмых конечно.
Что нас нужно от заглушки:
1) возможность старта на произвольном порту и передачи этого порта в код (сервер всегда localhost). Захардкодить порт можно, но тесты запускаются на CI конвейере, в т.ч. на используемом совместно сервере. И вообще не должны зависеть от среды запуска.
2) удобная возможность задавать разные ответы для разных path, например, method chaining
3) учёт параметров в URL
4) возможность задать http код ответа
5) возможность задать http заголовки ответа
6) возможность эмулировать таймаут
Ну и в идеале простая инициализация в тесте, например, с помощью аннотации.
И вот наконец кандидаты.
1) Wiremock. Главные плюсы - большое число возможностей, накопленных за время существования, и возможность работы как во встроенном режиме, так и standalone. Примеры фишек Wiremock - сценарный режим, когда можно задать ответ в зависимости от предыдущего запроса. Встроенный генератор случайных данных в полях ответа. Возможность использовать данные запроса в ответе (templating). Статья как подружить Wiremock с тестами https://www.baeldung.com/spring-webclient-wiremock-integration-testing
2) @RestClientTest - это «магическая» аннотация Spring Boot, автоматически связывающая добавленный в класс теста RestTemplate с сервером заглушки. Т.е. настройку порта делать не нужно. Вот статья по использованию https://www.baeldung.com/restclienttest-in-spring-boot
Если используете Spring и RestTemplate или его наследника RestClient - наверное оптимальный вариант. Для WebClient - асинхронщины и реактивщины - не годится, соответствующий тикет был отклонен командой Spring https://github.com/spring-projects/spring-boot/issues/8404
3) А рекомендует Spring для WebClient использовать MockWebServer. Вот статья https://howtodoinjava.com/java/library/mockwebserver-junit-webclient/
Из интересных фишек я заметил возможность получения информации о последнем запросе, пришедшем на сервер. И простейший сценарный режим, когда можно настроить очередь ответов для ряда последовательных запросов. Настраивать сложнее, чем предыдущие два варианта.
Итого: рекомендую @RestClientTest для синхронного Spring и Wiremock для остального.
#integration_tests #rare_test libs
Baeldung
Integration Testing Spring WebClient Using WireMock | Baeldung
Learn how to utilize WireMock API to stub HTTP-based client requests when using WebClient.
Всем привет!
Продолжим тему тестирования http взаимодействий. Кроме http клиента приложение может выставлять API. Этот пост про REST API, как наиболее распространённое. В мире победившего Spring endpoint обычно создаётся с помощью Spring @RestController, но в теории может быть и JAX-RS контроллер или что-то другое, даже «голый» сервлет.
Можно рассмотреть 4 случая.
1) модульное тестирование слоя контроллеров Spring приложения. В этом случае все сервисы в контроллере «мокаются», а Spring context загружается в ограниченном объёме, необходимом для эмуляции контроллера. Это кстати крутая фишка Spring - эмуляция контроллера без реального сетевого взаимодействия для модульных тестов. Ключевые слова - MockMvc и @WebMvcTest. Хорошее описание тут: https://sysout.ru/testirovanie-kontrollerov-s-pomoshhyu-mockmvc/, надо смотреть п. 2
Как я уже писал ранее - встроенные assert-ы MockMvc для json можно при желании заменить на ModelAssert, см. мой пост https://t.me/javaKotlinDevOps/343
2) интеграционное тестирование всех слоёв Spring приложения, но без сети. Вообще говоря, что считать интеграционным тестом - отдельная большая тема, но в большинстве статей, что я видел, такие тесты называются интеграционными. Используется тот же MockMvc, но уже с @SpringBootTest. Основное отличие - загружается весь Spring Context. Поэтому тест медленнее, чем больше приложение, тем медленнее. Если надо - в контексте можно поменять бины для тестового режима через Spring Profiles. Пример такого текста - в статье выше см. п. 1
3) интеграционное тестирование Spring приложения с сетью. Используется @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) и TestRestTemplate. Сервер для тестов запускается на произвольном порту, порт можно получить в тесте. Тесты этого типа ещё медленнее. Когда может быть полезен - нужно приближённое «к бою» тестирование. Или какие фишки http протестировать, типа редиректа. Вот пример https://sysout.ru/testirovanie-spring-boot-prilozheniya-s-testresttemplate/
А вот сравнение всех 3 подходов https://www.baeldung.com/spring-mockmvc-vs-webmvctest
4) у нас не Spring контроллер. Или уже есть опыт использования RestAssured, и он сугубо позитивный) Или нужны какие-то фишки RestAssured. А их много - xsd/json валидация, проверка длительности выполнения, встроенное логирование запроса и ответа, в т.ч. условное - в случае ошибки, использование Groovy для получения данных из тела запроса (!). Также данная библиотека рекомендуется для BDD тестов, т.к придерживается принятой там терминологии Given-When-Then https://en.m.wikipedia.org/wiki/Given-When-Then Для выполнения теста приложение нужно вначале запустить, например, через фазу pre-integration-test цикла сборки Maven. Вот неплохой tutorial https://www.baeldung.com/rest-assured-tutorial
Итого - как всегда в Java есть хорошие инструменты для разных задач.
#unittests #integration_test #rare_test_libs
Продолжим тему тестирования http взаимодействий. Кроме http клиента приложение может выставлять API. Этот пост про REST API, как наиболее распространённое. В мире победившего Spring endpoint обычно создаётся с помощью Spring @RestController, но в теории может быть и JAX-RS контроллер или что-то другое, даже «голый» сервлет.
Можно рассмотреть 4 случая.
1) модульное тестирование слоя контроллеров Spring приложения. В этом случае все сервисы в контроллере «мокаются», а Spring context загружается в ограниченном объёме, необходимом для эмуляции контроллера. Это кстати крутая фишка Spring - эмуляция контроллера без реального сетевого взаимодействия для модульных тестов. Ключевые слова - MockMvc и @WebMvcTest. Хорошее описание тут: https://sysout.ru/testirovanie-kontrollerov-s-pomoshhyu-mockmvc/, надо смотреть п. 2
Как я уже писал ранее - встроенные assert-ы MockMvc для json можно при желании заменить на ModelAssert, см. мой пост https://t.me/javaKotlinDevOps/343
2) интеграционное тестирование всех слоёв Spring приложения, но без сети. Вообще говоря, что считать интеграционным тестом - отдельная большая тема, но в большинстве статей, что я видел, такие тесты называются интеграционными. Используется тот же MockMvc, но уже с @SpringBootTest. Основное отличие - загружается весь Spring Context. Поэтому тест медленнее, чем больше приложение, тем медленнее. Если надо - в контексте можно поменять бины для тестового режима через Spring Profiles. Пример такого текста - в статье выше см. п. 1
3) интеграционное тестирование Spring приложения с сетью. Используется @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) и TestRestTemplate. Сервер для тестов запускается на произвольном порту, порт можно получить в тесте. Тесты этого типа ещё медленнее. Когда может быть полезен - нужно приближённое «к бою» тестирование. Или какие фишки http протестировать, типа редиректа. Вот пример https://sysout.ru/testirovanie-spring-boot-prilozheniya-s-testresttemplate/
А вот сравнение всех 3 подходов https://www.baeldung.com/spring-mockmvc-vs-webmvctest
4) у нас не Spring контроллер. Или уже есть опыт использования RestAssured, и он сугубо позитивный) Или нужны какие-то фишки RestAssured. А их много - xsd/json валидация, проверка длительности выполнения, встроенное логирование запроса и ответа, в т.ч. условное - в случае ошибки, использование Groovy для получения данных из тела запроса (!). Также данная библиотека рекомендуется для BDD тестов, т.к придерживается принятой там терминологии Given-When-Then https://en.m.wikipedia.org/wiki/Given-When-Then Для выполнения теста приложение нужно вначале запустить, например, через фазу pre-integration-test цикла сборки Maven. Вот неплохой tutorial https://www.baeldung.com/rest-assured-tutorial
Итого - как всегда в Java есть хорошие инструменты для разных задач.
#unittests #integration_test #rare_test_libs
SYSOUT
Тестирование контроллеров с помощью MockMvc - SYSOUT
Класс MockMvc предназначен для тестирования контроллеров. Он позволяет тестировать контроллеры без запуска http-сервера. То есть при выполнении тестов сетевое соединение не создается. С MockMvc можно писать как интеграционные тесты, так и unit-тесты. Ниже…
Всем привет!
Небольшое дополнение про Given When Then из предыдущего поста. Данная формулировка пошла из BDD тестирования - Behavior driven development. BDD - это приемочное тестирование. Но в модульных (юнит) тестах существует аналогичная концепция - AAA - Arrange - Act - Assert. Суть та же - тест делится на 3 этапа: подготовка данных - вызов тестируемого метода - проверка результата.
Обычно эти три части в тесте разделяются пустой строкой для читаемости. В случае RestAssured и method chainig так не выйдет, но стоит разделить этапы переводом на новую строку. Arrange опционален, Act и Assert - обязательны. Act и Assert стоит разделить, даже если тянется рука сразу проверить результат вызова метода.
#unittests
Небольшое дополнение про Given When Then из предыдущего поста. Данная формулировка пошла из BDD тестирования - Behavior driven development. BDD - это приемочное тестирование. Но в модульных (юнит) тестах существует аналогичная концепция - AAA - Arrange - Act - Assert. Суть та же - тест делится на 3 этапа: подготовка данных - вызов тестируемого метода - проверка результата.
Обычно эти три части в тесте разделяются пустой строкой для читаемости. В случае RestAssured и method chainig так не выйдет, но стоит разделить этапы переводом на новую строку. Arrange опционален, Act и Assert - обязательны. Act и Assert стоит разделить, даже если тянется рука сразу проверить результат вызова метода.
#unittests
Всем привет!
Есть такое известное правило - DRY - Don't Repeat Yourself. Оно касается кода, оно касается и настроек. Окей, задали мы настройки в одном месте - например, в корневом pom файле или build.gradle(.kts). Как пробросить их во все необходимые файлы при сборке? Этот процесс называется property expansion. К слову - в Maven есть понятие resource filtering. Это нечто большее - проход по всем ресурсным файлам, их фильтрация и кастомизация. Но обычно как раз таки используется для property expansion. Так вот, нашел неплохую статью как сделать это в Maven и Gradle https://www.baeldung.com/spring-boot-auto-property-expansion Что интересно - решение Maven выглядит более продуманным.
#gradle #maven #buildtool
Есть такое известное правило - DRY - Don't Repeat Yourself. Оно касается кода, оно касается и настроек. Окей, задали мы настройки в одном месте - например, в корневом pom файле или build.gradle(.kts). Как пробросить их во все необходимые файлы при сборке? Этот процесс называется property expansion. К слову - в Maven есть понятие resource filtering. Это нечто большее - проход по всем ресурсным файлам, их фильтрация и кастомизация. Но обычно как раз таки используется для property expansion. Так вот, нашел неплохую статью как сделать это в Maven и Gradle https://www.baeldung.com/spring-boot-auto-property-expansion Что интересно - решение Maven выглядит более продуманным.
#gradle #maven #buildtool
Baeldung
Automatic Property Expansion with Spring Boot | Baeldung
Learn about the property expansion mechanism provided by Spring through Maven and Gradle build methods.
Всем привет!
Давно не писал про Kotlin, а в названии канала он есть на почетном втором месте) Исправляюсь.
Основная фишка Kotlin - это упрощение написания и чтения кода за счет упрощения языка. В чем упрощение: все типы - объектные, функция всегда возвращает результат, нет неявных преобразований, нет проверяемых исключений, некоторые стандартные паттерны (синглтон, делегат) стали частью языка - не нужно изобретать велосипед. Возможность переопределения операций и полноценные функциональные типы - на самом деле тоже. Операция - краткий общеупотребительный вариант метода, функцию можно передавать как объект не создавая для этого объект.
Но как всегда есть нюансы.
Вот например inline методы и связанный с ним reified https://www.baeldung.com/kotlin/reified-functions
При беглом знакомстве возникают 2 вопроса:
1) разработчики Kotlin загрязняют язык, ведь компилятор, а скорее JVM, сами справятся с inline?
2) Kotlin хакнул type erasure для generic?
Ответ на оба вопроса - нет.
И есть отличная статья на эту тему https://habr.com/ru/articles/775120/ Автора знаю лично, рекомендую почитать эту и другие его статьи про Kotlin.
Для ленивых ))) ответы:
1) inline нужен только для методов с параметрами функционального типа, чтобы избежать обвертывания функции в объект. Java компилятор не умеет работать с функциональными типами, увы
2) reified не нарушает спецификации Java, компилятор Kotlin лишь сохраняет тип там, где он его знает, и это касается только inline методов
И про простоту Kotlin в целом и сложность inline. Как выглядит процесс со стороны:
1) у нас полноценные функциональные типы
2) в коде их будет много
3) Java не умеет с ними работать
4) сделаем inline, чтобы не снизить производительность при работе с такими типами
5) появляются баги из-за inline, приходится вводить ключевые слова noinline и crossinline. Подробнее об этом есть в статье выше.
6) кто-то просит: раз при inline мы знаем исходный тип generic - давайте дадим возможность работы с ним, появляется reified
7) возникают новые баги, их фиксят
...
Процесс вымышленный, возможно, в реальности было по-другому. Я хотел подчеркнуть вот что: одна фича тянет за собой другую, другая - несколько особых случаев. И все это усложняет язык, хотя цель была противоположная.
P.S. Ну и да, получается, во всем виновата Java)
#kotlin #java
Давно не писал про Kotlin, а в названии канала он есть на почетном втором месте) Исправляюсь.
Основная фишка Kotlin - это упрощение написания и чтения кода за счет упрощения языка. В чем упрощение: все типы - объектные, функция всегда возвращает результат, нет неявных преобразований, нет проверяемых исключений, некоторые стандартные паттерны (синглтон, делегат) стали частью языка - не нужно изобретать велосипед. Возможность переопределения операций и полноценные функциональные типы - на самом деле тоже. Операция - краткий общеупотребительный вариант метода, функцию можно передавать как объект не создавая для этого объект.
Но как всегда есть нюансы.
Вот например inline методы и связанный с ним reified https://www.baeldung.com/kotlin/reified-functions
При беглом знакомстве возникают 2 вопроса:
1) разработчики Kotlin загрязняют язык, ведь компилятор, а скорее JVM, сами справятся с inline?
2) Kotlin хакнул type erasure для generic?
Ответ на оба вопроса - нет.
И есть отличная статья на эту тему https://habr.com/ru/articles/775120/ Автора знаю лично, рекомендую почитать эту и другие его статьи про Kotlin.
Для ленивых ))) ответы:
1) inline нужен только для методов с параметрами функционального типа, чтобы избежать обвертывания функции в объект. Java компилятор не умеет работать с функциональными типами, увы
2) reified не нарушает спецификации Java, компилятор Kotlin лишь сохраняет тип там, где он его знает, и это касается только inline методов
И про простоту Kotlin в целом и сложность inline. Как выглядит процесс со стороны:
1) у нас полноценные функциональные типы
2) в коде их будет много
3) Java не умеет с ними работать
4) сделаем inline, чтобы не снизить производительность при работе с такими типами
5) появляются баги из-за inline, приходится вводить ключевые слова noinline и crossinline. Подробнее об этом есть в статье выше.
6) кто-то просит: раз при inline мы знаем исходный тип generic - давайте дадим возможность работы с ним, появляется reified
7) возникают новые баги, их фиксят
...
Процесс вымышленный, возможно, в реальности было по-другому. Я хотел подчеркнуть вот что: одна фича тянет за собой другую, другая - несколько особых случаев. И все это усложняет язык, хотя цель была противоположная.
P.S. Ну и да, получается, во всем виновата Java)
#kotlin #java
Baeldung on Kotlin
Reified Functions in Kotlin | Baeldung on Kotlin
Learn how to use reified functions in Kotlin
Всем привет!
Одна из моих любимых тем: разработка - искусство компромиссов. Поиск по тэгам #dev_compromises и #arch_compromises. Следствие этого подхода, принцип, который я бы назвал - "не все так однозначно".
Вопрос - как вы относитесь к рефлексии в Java?
Досрочный ответ: рефлексия - это плохо, лучше не использовать. Если дошло до рефлексии - значит в архитектуре проблема.
Чтобы лучше разобраться в теме надо ответить на вопрос: а почему плохо?
Ответа два:
1) рефлексия позволяет нарушить принципы ООП и, следовательно, архитектуру приложения. Автор скрыл внутренности класса через private, а мы туда лезем своими "грязными руками")))
2) снижение производительности. Тут частично работает тот факт, что делаются лишние вызовы кода. Но самое главное - JIT компилятор плохо умеет оптимизировать такой код, т.к. он слишком динамический. Изменится может сам класс, который приходит на вход метода с рефлексией
Окей, инкапсуляция нарушается, код работает медленно. Не используем?
А что с поиском аннотаций по коду? Не своих - до них мы еще дойдем - чужих, чтобы получить некие метаданные об объекте. Большинство вариантов вот тут основано на рефлексии https://www.baeldung.com/java-scan-annotations-runtime
Или у нас есть аннотации, созданные с помощью Spring AOP - это проще, чем AspectJ, если у вас используется Spring. А Spring AOP использует динамические прокси, создаваемые в runtime https://www.baeldung.com/spring-aop-vs-aspectj А с помощью чего создается прокси в runtime - правильно, рефлексии.
Да что там AOP - создание бинов из @Configuration - это тоже вызов методов @Bean через рефлексию.
Почему же рефлексию используют и предлагают к использованию, если это такая проблемная технология?
Вернемся к двум ее недостаткам:
1) не надо использовать рефлексию для вызова private методов или доступа к private полям. Если такая задача встала - у вас в самом деле проблемы с архитектурой
2) не надо использовать рефлексию часто и при этом в высоконагруженных приложениях. Тот же Spring использует рефлексию только при старте приложения для инициализации прокси и бинов. И к слову в т.ч. и поэтому старт Spring приложения может быть долгим, и люди с этим борются, см. мой цикл статей про ускорение старта #java_start_boost Более того, разработчикам Spring для поддержки native image пришлось серьезно допиливать его в т.ч. из-за динамических прокси и @Configuration https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html
Итого: рефлексию стоит рассматривать как возможность получить метаданные о классе. Помня при этом о производительности.
И если вопрос упирается в производительность - всегда есть альтернативы рефлексии. Это работа с байт-кодом не в runtime:
1) compile time (свой компилятор, см. статью про AspectJ)
2) post-compile time (свой плагин для сборки, см. Jandex для поиска аннотаций https://smallrye.io/jandex/jandex/3.2.2/index.html)
3) load-time (свой агент+classloader, см. статью про AspectJ)
Все варианты сложнее в реализации и подключении к проекту, зато вносят минимальное влияние в runtime.
P.S. Да, если при упоминании о динамических прокси вы вспомнили про задачку с собесов о вложенном @Transactional - это оно. И ответ на этот вопрос не так очевиден https://habr.com/ru/articles/347752/
#java #reflection #dev_compromises
Одна из моих любимых тем: разработка - искусство компромиссов. Поиск по тэгам #dev_compromises и #arch_compromises. Следствие этого подхода, принцип, который я бы назвал - "не все так однозначно".
Вопрос - как вы относитесь к рефлексии в Java?
Досрочный ответ: рефлексия - это плохо, лучше не использовать. Если дошло до рефлексии - значит в архитектуре проблема.
Чтобы лучше разобраться в теме надо ответить на вопрос: а почему плохо?
Ответа два:
1) рефлексия позволяет нарушить принципы ООП и, следовательно, архитектуру приложения. Автор скрыл внутренности класса через private, а мы туда лезем своими "грязными руками")))
2) снижение производительности. Тут частично работает тот факт, что делаются лишние вызовы кода. Но самое главное - JIT компилятор плохо умеет оптимизировать такой код, т.к. он слишком динамический. Изменится может сам класс, который приходит на вход метода с рефлексией
Окей, инкапсуляция нарушается, код работает медленно. Не используем?
А что с поиском аннотаций по коду? Не своих - до них мы еще дойдем - чужих, чтобы получить некие метаданные об объекте. Большинство вариантов вот тут основано на рефлексии https://www.baeldung.com/java-scan-annotations-runtime
Или у нас есть аннотации, созданные с помощью Spring AOP - это проще, чем AspectJ, если у вас используется Spring. А Spring AOP использует динамические прокси, создаваемые в runtime https://www.baeldung.com/spring-aop-vs-aspectj А с помощью чего создается прокси в runtime - правильно, рефлексии.
Да что там AOP - создание бинов из @Configuration - это тоже вызов методов @Bean через рефлексию.
Почему же рефлексию используют и предлагают к использованию, если это такая проблемная технология?
Вернемся к двум ее недостаткам:
1) не надо использовать рефлексию для вызова private методов или доступа к private полям. Если такая задача встала - у вас в самом деле проблемы с архитектурой
2) не надо использовать рефлексию часто и при этом в высоконагруженных приложениях. Тот же Spring использует рефлексию только при старте приложения для инициализации прокси и бинов. И к слову в т.ч. и поэтому старт Spring приложения может быть долгим, и люди с этим борются, см. мой цикл статей про ускорение старта #java_start_boost Более того, разработчикам Spring для поддержки native image пришлось серьезно допиливать его в т.ч. из-за динамических прокси и @Configuration https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html
Итого: рефлексию стоит рассматривать как возможность получить метаданные о классе. Помня при этом о производительности.
И если вопрос упирается в производительность - всегда есть альтернативы рефлексии. Это работа с байт-кодом не в runtime:
1) compile time (свой компилятор, см. статью про AspectJ)
2) post-compile time (свой плагин для сборки, см. Jandex для поиска аннотаций https://smallrye.io/jandex/jandex/3.2.2/index.html)
3) load-time (свой агент+classloader, см. статью про AspectJ)
Все варианты сложнее в реализации и подключении к проекту, зато вносят минимальное влияние в runtime.
P.S. Да, если при упоминании о динамических прокси вы вспомнили про задачку с собесов о вложенном @Transactional - это оно. И ответ на этот вопрос не так очевиден https://habr.com/ru/articles/347752/
#java #reflection #dev_compromises
Baeldung
Scanning Java Annotations at Runtime | Baeldung
Learn about scanning Java annotations at runtime.
Всем привет!
Как можно масштабировать нагрузку в облаке? Под облаком я понимаю k8s как некий стандарт.
1) "ручками". Простой, но не надежный способ. Отягощающий фактор - не всегда у сопровождения ПРОМ есть права на смену настроек Deployment, тогда требуется отдельный деплой
2) выставить число подов и limits, соответствующими максимальной нагрузке. Еще проще, но совсем не рационально в плане использования ресурсов
3) HorizontalPodAutoscaler https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ Позволяет менять число подов сервиса при превышении некого порога нагрузки по cpu и memory. Порог - это не процент использования, а соотношения заданного requests с текущим значением. Подходит если требуемая нагрузка может превышать возможности 1 пода, и если сервис поддерживает горизонтальное масштабирование - т.е. stateless.
4) VerticalPodAutoscaler https://habr.com/ru/companies/flant/articles/541642/ Как следует из название - масштабирует поды вертикально. Тут есть нюанс - на данный момент k8s не позволяет менять requests и limits у пода "на ходу". Данная фича называется In-Place Update of Pod Resources и пока находится в бета-версии https://github.com/kubernetes/enhancements/issues/1287
Сейчас VerticalPodAutoscaler может следующее:
а) на основе истории использования подом ресурсов рассчитать и выдать рекомендуемые значения
б) применить эти значения при очередном плановом рестарте
в) самому рестартовать под
Подходит для следующих случаев:
а) statefull сервисы типа хранилища данных в облаке StatefullSet, для которых горизонтальное масштабирование или невозможно, или не приветствуется
б) "легкие" сервисы, которым достаточно одной реплики (или двух по требованиям надежности)
Из особенностей - не гарантируется корректная работа совместно с HorizontalPodAutoscaler по одним и тем же метрикам cpu и memory. И не работает с Java приложениями в плане нагрузки по памяти, т.к. Java сама управляет памятью, а VerticalPodAutoscaler эти данные не видит.
Ну и главный минус - необходимость рестарта для применения настроек
5) как избавиться от необходимости рестарта? Дождаться полноценного внедрения In-Place Update of Pod Resources и доработки VerticalPodAutoscaler. Или использовать бета-версию In-Place Update of Pod Resources реализовать соответствующий k8s controller самому. Это уже сделано: https://piotrminkowski.com/2023/08/22/resize-cpu-limit-to-speed-up-java-startup-on-kubernetes/
Данная реализация не умеет сама рассчитывать рекомендуемые значения requests. Зато позволяет красиво проблему JVM приложений, о которой я уже писал ранее - проблему долгого старта. Java, а точнее Spring приложение, при старте производит достаточно ресурсоемкую настройку контекста. Речь идет про обычный Spring Boot без применения всех возможных способов ускорить запуск. Если память после первоначальной настройки так и остается занятой созданными бинами, то cpu при старте требуется сильно больше, чем при обычной работе. Что с этим делать?
а) Не выделять лишние cpu - время старта увеличивается в разы
б) Пойти по пути, описанному в п.2 - выдать максимум ресурсов. Тогда может получиться, что мы тратим cpu на быстрый старт приложения, а далее ресурсы не утилизируются
в) In-Place Update of Pod Resources
Конкретно по данной реализации - мне она кажется не идеальной, т.к. ресурс по cpu настраивается в 2 манифестах, это не наглядно и способствует внесению несогласованных правок. Но решение в целом - полезное.
#k8s #cloud #scalability
Как можно масштабировать нагрузку в облаке? Под облаком я понимаю k8s как некий стандарт.
1) "ручками". Простой, но не надежный способ. Отягощающий фактор - не всегда у сопровождения ПРОМ есть права на смену настроек Deployment, тогда требуется отдельный деплой
2) выставить число подов и limits, соответствующими максимальной нагрузке. Еще проще, но совсем не рационально в плане использования ресурсов
3) HorizontalPodAutoscaler https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ Позволяет менять число подов сервиса при превышении некого порога нагрузки по cpu и memory. Порог - это не процент использования, а соотношения заданного requests с текущим значением. Подходит если требуемая нагрузка может превышать возможности 1 пода, и если сервис поддерживает горизонтальное масштабирование - т.е. stateless.
4) VerticalPodAutoscaler https://habr.com/ru/companies/flant/articles/541642/ Как следует из название - масштабирует поды вертикально. Тут есть нюанс - на данный момент k8s не позволяет менять requests и limits у пода "на ходу". Данная фича называется In-Place Update of Pod Resources и пока находится в бета-версии https://github.com/kubernetes/enhancements/issues/1287
Сейчас VerticalPodAutoscaler может следующее:
а) на основе истории использования подом ресурсов рассчитать и выдать рекомендуемые значения
б) применить эти значения при очередном плановом рестарте
в) самому рестартовать под
Подходит для следующих случаев:
а) statefull сервисы типа хранилища данных в облаке StatefullSet, для которых горизонтальное масштабирование или невозможно, или не приветствуется
б) "легкие" сервисы, которым достаточно одной реплики (или двух по требованиям надежности)
Из особенностей - не гарантируется корректная работа совместно с HorizontalPodAutoscaler по одним и тем же метрикам cpu и memory. И не работает с Java приложениями в плане нагрузки по памяти, т.к. Java сама управляет памятью, а VerticalPodAutoscaler эти данные не видит.
Ну и главный минус - необходимость рестарта для применения настроек
5) как избавиться от необходимости рестарта? Дождаться полноценного внедрения In-Place Update of Pod Resources и доработки VerticalPodAutoscaler. Или использовать бета-версию In-Place Update of Pod Resources реализовать соответствующий k8s controller самому. Это уже сделано: https://piotrminkowski.com/2023/08/22/resize-cpu-limit-to-speed-up-java-startup-on-kubernetes/
Данная реализация не умеет сама рассчитывать рекомендуемые значения requests. Зато позволяет красиво проблему JVM приложений, о которой я уже писал ранее - проблему долгого старта. Java, а точнее Spring приложение, при старте производит достаточно ресурсоемкую настройку контекста. Речь идет про обычный Spring Boot без применения всех возможных способов ускорить запуск. Если память после первоначальной настройки так и остается занятой созданными бинами, то cpu при старте требуется сильно больше, чем при обычной работе. Что с этим делать?
а) Не выделять лишние cpu - время старта увеличивается в разы
б) Пойти по пути, описанному в п.2 - выдать максимум ресурсов. Тогда может получиться, что мы тратим cpu на быстрый старт приложения, а далее ресурсы не утилизируются
в) In-Place Update of Pod Resources
Конкретно по данной реализации - мне она кажется не идеальной, т.к. ресурс по cpu настраивается в 2 манифестах, это не наглядно и способствует внесению несогласованных правок. Но решение в целом - полезное.
#k8s #cloud #scalability
Kubernetes
Horizontal Pod Autoscaling
In Kubernetes, a HorizontalPodAutoscaler automatically updates a workload resource (such as a Deployment or StatefulSet), with the aim of automatically scaling the workload to match demand.
Horizontal scaling means that the response to increased load is to…
Horizontal scaling means that the response to increased load is to…
Всем привет!
Не так давно ChatGPT начала тестировать "умный поиск" - назвав его SearchGPT. В отличие от AI чата умный поиск ищет по существующим в интернете источникам, после чего делает выжимку из источника, оставляя ссылку на него. Что-то знакомое... Да, именно так работает Perplexity, см. https://t.me/javaKotlinDevOps/331. При этом исходный запрос может быть на естественном языке, и есть этап преобразования запроса в вид, оптимальный для поиска. И еще важно - учитывается контекст разговора, т.е. предыдущие запросы. Итого получаем смесь чата и поиска. Главное отличие от чатов - некая гарантия, что ответ не был синтезирован. Как этого удается добиться - очень интересный вопрос, ответа на него не знаю)
Но важно другое - на мой взгляд поиск в интернете именно так и должен выглядеть. Краткая выжимка, ссылки, и только если недостаточно информации - идем по ссылкам.
Чем ответят на это поисковики?
У Google и Bing пока не вижу ничего подобного. Я не про возможность добавить ссылки для ответа чат-бота, а про интеграцию в поиск и некие гарантии точности ответа. Хотя возможности у них есть - Gemini и Copylot соответственно.
А вот Yandex совместил поиск с чатом, и это можно увидеть выбрав тип поиска Нейро. Подробнее как это работает - см. https://vk.com/video-17796776_456241214 Это не бета, не ограниченный доступ.
Круто, что могу сказать!
P.S. Perplexity похоже задал тренд.
P.P.S. Как все-таки они гарантируют отсутствие галюцинаций???
#ai #search
Не так давно ChatGPT начала тестировать "умный поиск" - назвав его SearchGPT. В отличие от AI чата умный поиск ищет по существующим в интернете источникам, после чего делает выжимку из источника, оставляя ссылку на него. Что-то знакомое... Да, именно так работает Perplexity, см. https://t.me/javaKotlinDevOps/331. При этом исходный запрос может быть на естественном языке, и есть этап преобразования запроса в вид, оптимальный для поиска. И еще важно - учитывается контекст разговора, т.е. предыдущие запросы. Итого получаем смесь чата и поиска. Главное отличие от чатов - некая гарантия, что ответ не был синтезирован. Как этого удается добиться - очень интересный вопрос, ответа на него не знаю)
Но важно другое - на мой взгляд поиск в интернете именно так и должен выглядеть. Краткая выжимка, ссылки, и только если недостаточно информации - идем по ссылкам.
Чем ответят на это поисковики?
У Google и Bing пока не вижу ничего подобного. Я не про возможность добавить ссылки для ответа чат-бота, а про интеграцию в поиск и некие гарантии точности ответа. Хотя возможности у них есть - Gemini и Copylot соответственно.
А вот Yandex совместил поиск с чатом, и это можно увидеть выбрав тип поиска Нейро. Подробнее как это работает - см. https://vk.com/video-17796776_456241214 Это не бета, не ограниченный доступ.
Круто, что могу сказать!
P.S. Perplexity похоже задал тренд.
P.P.S. Как все-таки они гарантируют отсутствие галюцинаций???
#ai #search
Telegram
(java || kotlin) && devOps
Всем привет!
Запилил небольшое сравнение AI чатов для задач разработки.
https://gitverse.ru/javadev/ai-tools-comparision/content/master/README.md
Почему в Git - потому что там есть полноценный Markdown и таблицы.
Фокус на бесплатных инструментах - для тех…
Запилил небольшое сравнение AI чатов для задач разработки.
https://gitverse.ru/javadev/ai-tools-comparision/content/master/README.md
Почему в Git - потому что там есть полноценный Markdown и таблицы.
Фокус на бесплатных инструментах - для тех…
Всем привет!
Есть такая отличная IDE для Java и Kotlin - IntelliJ IDEA.
Лучшая если быть точным)
И у нее есть 2 версии: Community и Ultimate.
Отличия можно посмотреть тут https://www.jetbrains.com/products/compare/?product=idea&product=idea-ce
Плохая новость в том, что IntelliJ, к сожалению, забила на российских разработчиков. Если Community можно скачать без VPN
// а) пока можно б) VPN понадобится для обновления плагинов)
то купить Ultimate сложнее.
Небольшое отступление - даже если вас устраивает Community - рекомендую прочитать пост и походить по ссылкам, найдете полезные плагины.
Так вот, если смотреть на отличия двух версий IDEA - бросаются в глаза следующие полезные штуки:
1) поддержка Spring
2) поддержка JPA
3) Database Tools
4) HTTP Client
5) поддержка Kubernetes
6) Java applications servers
7) Profiling tools
Что тут можно сделать?
-1) сидеть на perpetual лицензии. Плохо, не доступны новые версии Java и фиксы
0) даунгрейд до Community - возможно кому-то подойдет, но есть варианты лучше
1) найти правильную карту или человека с правильной картой и надеяться, что вас не заблокируют - https://t.me/spring_aio/245. Так себе идея, как по мне.
2) использовать, скажем так, альтернативные способы получения лицензии. Еще пару лет назад никогда бы не порекомендовал такой способ, но в данном конкретном случае, учитывая пренебрежение к разработчикам своей страны - да, можно)
3) в качестве замены IDEA Ultimate Сбер разрабатывает GigaIDE Desktop. Важное слово здесь - разрабатывает.
Что это за зверь такой - если кратко: IDEA Community + open source плагины + предустановленный AI ассистент GigaCode. Подробнее можно почитать тут https://habr.com/ru/companies/haulmont/articles/828828/ Статья еще полезна тем, что можно подсмотреть полезные плагины, которые предустановлены в GigaIDE.
Обращает внимание нормальная альтернатива Database Tools и отсутствие альтернатив для остальных пунктов. Самая больная часть - Spring. Разработка плагина от Сбера идет, результат на данный момент неизвестен.
4) собрать свою IDEA на основе Community или GigaIDE. И тут у нас есть хорошая альтернатива Spring и JPA плагину - Amplicode. Это новый плагин от Haulmont, авторов JPA Buddy - плагина для работы с JPA+Liquibase+Flyway. Amplicode насколько я могу судить включает в себя функционал JPA Buddy. Детальнее про Amplicode можно почитать тут https://habr.com/ru/companies/haulmont/articles/814207/ или посмотреть видосы тут https://vk.com/video/playlist/-222549074_1 А вот тут независимые от разработчиков Amplicode ребята провели сравнение поддержки Spring в Ultimate и Amplicode https://habr.com/ru/companies/spring_aio/articles/854062/
Они примерно равны, со своими сильными и слабыми сторонами. Лично мне Amplicode даже больше понравился. Разве что встроенного аналога start.spring.io не хватает, но в конце концов есть собственно https://start.spring.io. В общем настоятельно рекомендую установить Amplicode даже если у вас Ultimate. Предварительно посмотрев обучающие материалы по ссылке выше)
5) хардкор - собрать свою IDEA из исходников, вот инструкция https://habr.com/ru/companies/spring_aio/articles/852526/ Как по мне - особой необходимости нет. Пока Community Edition останется Open Source - я бы не беспокоился. Вряд ли в open source продукт вкорячат блокировку по IP) И вряд ли перестанут работать все VPN, включая настроенные самостоятельно.
P.S. Что касается остальных инструментов из Community - если знаете альтернативы - добро пожаловать в комментарии.
Или возможно я забыл что еще важное?
#ide #idea
Есть такая отличная IDE для Java и Kotlin - IntelliJ IDEA.
Лучшая если быть точным)
И у нее есть 2 версии: Community и Ultimate.
Отличия можно посмотреть тут https://www.jetbrains.com/products/compare/?product=idea&product=idea-ce
Плохая новость в том, что IntelliJ, к сожалению, забила на российских разработчиков. Если Community можно скачать без VPN
// а) пока можно б) VPN понадобится для обновления плагинов)
то купить Ultimate сложнее.
Небольшое отступление - даже если вас устраивает Community - рекомендую прочитать пост и походить по ссылкам, найдете полезные плагины.
Так вот, если смотреть на отличия двух версий IDEA - бросаются в глаза следующие полезные штуки:
1) поддержка Spring
2) поддержка JPA
3) Database Tools
4) HTTP Client
5) поддержка Kubernetes
6) Java applications servers
7) Profiling tools
Что тут можно сделать?
-1) сидеть на perpetual лицензии. Плохо, не доступны новые версии Java и фиксы
0) даунгрейд до Community - возможно кому-то подойдет, но есть варианты лучше
1) найти правильную карту или человека с правильной картой и надеяться, что вас не заблокируют - https://t.me/spring_aio/245. Так себе идея, как по мне.
2) использовать, скажем так, альтернативные способы получения лицензии. Еще пару лет назад никогда бы не порекомендовал такой способ, но в данном конкретном случае, учитывая пренебрежение к разработчикам своей страны - да, можно)
3) в качестве замены IDEA Ultimate Сбер разрабатывает GigaIDE Desktop. Важное слово здесь - разрабатывает.
Что это за зверь такой - если кратко: IDEA Community + open source плагины + предустановленный AI ассистент GigaCode. Подробнее можно почитать тут https://habr.com/ru/companies/haulmont/articles/828828/ Статья еще полезна тем, что можно подсмотреть полезные плагины, которые предустановлены в GigaIDE.
Обращает внимание нормальная альтернатива Database Tools и отсутствие альтернатив для остальных пунктов. Самая больная часть - Spring. Разработка плагина от Сбера идет, результат на данный момент неизвестен.
4) собрать свою IDEA на основе Community или GigaIDE. И тут у нас есть хорошая альтернатива Spring и JPA плагину - Amplicode. Это новый плагин от Haulmont, авторов JPA Buddy - плагина для работы с JPA+Liquibase+Flyway. Amplicode насколько я могу судить включает в себя функционал JPA Buddy. Детальнее про Amplicode можно почитать тут https://habr.com/ru/companies/haulmont/articles/814207/ или посмотреть видосы тут https://vk.com/video/playlist/-222549074_1 А вот тут независимые от разработчиков Amplicode ребята провели сравнение поддержки Spring в Ultimate и Amplicode https://habr.com/ru/companies/spring_aio/articles/854062/
Они примерно равны, со своими сильными и слабыми сторонами. Лично мне Amplicode даже больше понравился. Разве что встроенного аналога start.spring.io не хватает, но в конце концов есть собственно https://start.spring.io. В общем настоятельно рекомендую установить Amplicode даже если у вас Ultimate. Предварительно посмотрев обучающие материалы по ссылке выше)
5) хардкор - собрать свою IDEA из исходников, вот инструкция https://habr.com/ru/companies/spring_aio/articles/852526/ Как по мне - особой необходимости нет. Пока Community Edition останется Open Source - я бы не беспокоился. Вряд ли в open source продукт вкорячат блокировку по IP) И вряд ли перестанут работать все VPN, включая настроенные самостоятельно.
P.S. Что касается остальных инструментов из Community - если знаете альтернативы - добро пожаловать в комментарии.
Или возможно я забыл что еще важное?
#ide #idea
JetBrains
JetBrains Products Comparison
Всем привет.
В дополнение к предыдущему посту - ещё один способ получить IDEA Ultimate в России.
6) участие в программе EAP - Early Access Program. https://www.jetbrains.com/idea/nextversion
За подсказку спасибо @poeticpragmatic
Особенности:
а) критичные баги не замечены
б) имеет смысл автоматизировать накат новой версии скриптом с сохранением предыдущей для возможности отката. Если критичные баги все же появятся)
Не заблокируют ли этот вариант - да кто его знает)
#ide #idea
В дополнение к предыдущему посту - ещё один способ получить IDEA Ultimate в России.
6) участие в программе EAP - Early Access Program. https://www.jetbrains.com/idea/nextversion
За подсказку спасибо @poeticpragmatic
Особенности:
а) критичные баги не замечены
б) имеет смысл автоматизировать накат новой версии скриптом с сохранением предыдущей для возможности отката. Если критичные баги все же появятся)
Не заблокируют ли этот вариант - да кто его знает)
#ide #idea
JetBrains
Early Access Program (EAP) - IntelliJ IDEA
Code-centric IDE, focused on your productivity. Full Java EE support, deep code understanding, best debugger, refactorings, everything right out of the box...
Всем привет!
И ещё пару мыслей по развитию IDEA в России.
1) кажется, что со стороны Сбера было бы разумнее использовать готовый сильный продукт в виде Amplicode, чем в спешном порядке делать свой Spring плагин. Даже с учётом потенциальной монетизации Amplicode.
2) Haulmont активно развивается. Вначале JPA Buddy, теперь Amplicode, включающий его функционал. Что дальше?
IDEA, к слову, начинала с 2 плагинов к Borland JBuilder https://blog.jetbrains.com/ru/team/2013/02/28/nikolaj-chashnikov-intellij-idea-ide-kotoraya-ponimaet-kod/
Да, эти два пути противоречат друг другу. Да и российский рынок не выдержит 2 IDE) Их в мире по большому счёту три.
#ide #idea
И ещё пару мыслей по развитию IDEA в России.
1) кажется, что со стороны Сбера было бы разумнее использовать готовый сильный продукт в виде Amplicode, чем в спешном порядке делать свой Spring плагин. Даже с учётом потенциальной монетизации Amplicode.
2) Haulmont активно развивается. Вначале JPA Buddy, теперь Amplicode, включающий его функционал. Что дальше?
IDEA, к слову, начинала с 2 плагинов к Borland JBuilder https://blog.jetbrains.com/ru/team/2013/02/28/nikolaj-chashnikov-intellij-idea-ide-kotoraya-ponimaet-kod/
Да, эти два пути противоречат друг другу. Да и российский рынок не выдержит 2 IDE) Их в мире по большому счёту три.
#ide #idea
The JetBrains Blog
IntelliJ IDEA — IDE, которая понимает код | Блог JetBrains
Николай Чашников рассказывает о разных сторонах IntelliJ IDEA
Всем привет!
Давно хотел написать небольшую памятку по уровням логирования.
Стандартный набор уровней логирования для бизнес приложений:
TRACE
DEBUG
INFO
WARNING
ERROR
Встречается еще один - FATAL - но на моей практике это что-то разряда OutOfMemory, которое прикладной код не выбрасывает.
TRACE - самый редко встречающийся уровень. Почему? Потому что всегда, когда стоит выбор между трассировкой через логи или запуском в режиме отладчика - нужно выбирать второе. Это банально быстрее, т.к. не требует пересборок. Почему именно пересборки, во множественном числе - потому что у нас ООП, куча классов и методов, с первого раза расставить TRACE правильно вряд ли получится. Единственный минус отладки - если это происходит на тестовом или dev стенде - другие люди не смогут там работать. Т.е. их нужно предупредить. Или еще лучше - иметь минимум 2 подходящих стенда. Когда стенд не подходящий - это ПРОМ (PROD) или PROD-like стенды, куда нет сетевого доступа с компьютера разработчика. Вот это пожалуй единственное место, где может понадобиться TRACE. И если он там понадобился, то возможно у вас проблемы либо на этапе тестирования. Либо какие-то значимые отличия в конфигурации стендов, что тоже может быть поводом задуматься.
Что делать с TRACE - убирать сразу после нахождения проблемы, т.е. в следующем хотфиксе.
DEBUG - используется как правило на тестовых стендах чтобы разобраться с пришедшими от смежников данными и как эти данные влияют на состояние нашего сервиса. Разработка и отладка идет на заглушках, что там придет от смежников до конца не ясно, несмотря на согласованную аналитику - вот этот вот кейс. Если используется для трассировки шагов - см. абзац выше)
Что делать с DEBUG - убирать перед фиксацией ПРОМ релиза. Почему бы не оставить? Даже не из-за производительности, этот момент решить можно, а из-за ухудшения читаемости. Размер кода увеличивается, лог как правило дублирует близлежащий метод в плане доносимой информации, т.е. нарушаем DRY. Если DEBUG во внешней библиотеке - отключаем через настройку уровня логирования для конкретного пакета.
Все уровни далее по большому счету пишутся для сопровождения и тестировщиков.
INFO - нет ошибки, но сервис дошел до некой важной точки, и эта информация поможет при разборе полетов в случае ошибок и сбоев. Я видел кейсы, когда сопровождение запрещало писать на ПРОМ с уровнем INFO, но со временем оно одумывалось, и запрет снимали).
WARN - ошибки, не позволяющей вернуть данные клиенту, нет. Но есть либо некритичная ошибка, либо отсутствуют какие-то данные. И поэтому мы либо уходим в альтернативную ветку пользовательского сценария, либо берем данные из кэша, и возвращаем ответ клиенту.
ERROR - ошибка, прокидываемая клиенту. Не в виде stack trace конечно) Суть в том, что процесс обработки клиентского запроса прерывается, возвращается ошибка согласно API. Две самые частые ошибки, что я здесь вижу:
а) вывод error на любую ошибку смежника, даже не блокирующую.
б) error из используемой библиотеки, которая для нашего процесса не является блокирующей. В этом случае ее нужно убирать либо через настройку уровня логирования для пакета этой библиотеки, либо через ее доработку (если это возможно).
Эти три уровня убирать перед релизом не нужно, но стоит периодически их просматривать на предмет актуальности.
И последнее - по производительности. Просадка в производительности из-за логирования может быть из-за вычисления параметров даже в том случае, когда текущий уровень логирования выключен. Спасает ленивое вычисление параметров для выводимого в лог сообщения.
Это поддерживается в
а) log4j https://logging.apache.org/log4j/2.12.x/manual/api.html
б) slf4j https://www.slf4j.org/faq.html#logging_performance
через lambda параметры начиная с Java 8. Т.е. в большинстве инсталляций. Ну я надеюсь на это)
Еще просадка может быть из-за количества сообщений. Тогда смотри абзацы про TRACE и DEBUG выше. Еще можно глянуть мой пост про производительность в log4j https://t.me/javaKotlinDevOps/77 и поднять уровень логирования для ПРОМ.
#java #logging
Давно хотел написать небольшую памятку по уровням логирования.
Стандартный набор уровней логирования для бизнес приложений:
TRACE
DEBUG
INFO
WARNING
ERROR
Встречается еще один - FATAL - но на моей практике это что-то разряда OutOfMemory, которое прикладной код не выбрасывает.
TRACE - самый редко встречающийся уровень. Почему? Потому что всегда, когда стоит выбор между трассировкой через логи или запуском в режиме отладчика - нужно выбирать второе. Это банально быстрее, т.к. не требует пересборок. Почему именно пересборки, во множественном числе - потому что у нас ООП, куча классов и методов, с первого раза расставить TRACE правильно вряд ли получится. Единственный минус отладки - если это происходит на тестовом или dev стенде - другие люди не смогут там работать. Т.е. их нужно предупредить. Или еще лучше - иметь минимум 2 подходящих стенда. Когда стенд не подходящий - это ПРОМ (PROD) или PROD-like стенды, куда нет сетевого доступа с компьютера разработчика. Вот это пожалуй единственное место, где может понадобиться TRACE. И если он там понадобился, то возможно у вас проблемы либо на этапе тестирования. Либо какие-то значимые отличия в конфигурации стендов, что тоже может быть поводом задуматься.
Что делать с TRACE - убирать сразу после нахождения проблемы, т.е. в следующем хотфиксе.
DEBUG - используется как правило на тестовых стендах чтобы разобраться с пришедшими от смежников данными и как эти данные влияют на состояние нашего сервиса. Разработка и отладка идет на заглушках, что там придет от смежников до конца не ясно, несмотря на согласованную аналитику - вот этот вот кейс. Если используется для трассировки шагов - см. абзац выше)
Что делать с DEBUG - убирать перед фиксацией ПРОМ релиза. Почему бы не оставить? Даже не из-за производительности, этот момент решить можно, а из-за ухудшения читаемости. Размер кода увеличивается, лог как правило дублирует близлежащий метод в плане доносимой информации, т.е. нарушаем DRY. Если DEBUG во внешней библиотеке - отключаем через настройку уровня логирования для конкретного пакета.
Все уровни далее по большому счету пишутся для сопровождения и тестировщиков.
INFO - нет ошибки, но сервис дошел до некой важной точки, и эта информация поможет при разборе полетов в случае ошибок и сбоев. Я видел кейсы, когда сопровождение запрещало писать на ПРОМ с уровнем INFO, но со временем оно одумывалось, и запрет снимали).
WARN - ошибки, не позволяющей вернуть данные клиенту, нет. Но есть либо некритичная ошибка, либо отсутствуют какие-то данные. И поэтому мы либо уходим в альтернативную ветку пользовательского сценария, либо берем данные из кэша, и возвращаем ответ клиенту.
ERROR - ошибка, прокидываемая клиенту. Не в виде stack trace конечно) Суть в том, что процесс обработки клиентского запроса прерывается, возвращается ошибка согласно API. Две самые частые ошибки, что я здесь вижу:
а) вывод error на любую ошибку смежника, даже не блокирующую.
б) error из используемой библиотеки, которая для нашего процесса не является блокирующей. В этом случае ее нужно убирать либо через настройку уровня логирования для пакета этой библиотеки, либо через ее доработку (если это возможно).
Эти три уровня убирать перед релизом не нужно, но стоит периодически их просматривать на предмет актуальности.
И последнее - по производительности. Просадка в производительности из-за логирования может быть из-за вычисления параметров даже в том случае, когда текущий уровень логирования выключен. Спасает ленивое вычисление параметров для выводимого в лог сообщения.
Это поддерживается в
а) log4j https://logging.apache.org/log4j/2.12.x/manual/api.html
б) slf4j https://www.slf4j.org/faq.html#logging_performance
через lambda параметры начиная с Java 8. Т.е. в большинстве инсталляций. Ну я надеюсь на это)
Еще просадка может быть из-за количества сообщений. Тогда смотри абзацы про TRACE и DEBUG выше. Еще можно глянуть мой пост про производительность в log4j https://t.me/javaKotlinDevOps/77 и поднять уровень логирования для ПРОМ.
#java #logging
logging.apache.org
Log4j – Log4j 2 API - Apache Log4j 2
Всем привет!
Продолжим про логи. Какие требования к ним предъявляются?
Для начала поговорим про разработку:
0) библиотека логирования. Очевидная вещь - для кода, работающего в ПРОМе, логи пишутся только через библиотеку логирования. Т.к. библиотека позволяет настраивать место хранения, формат, фильтрацию логов и многое другое.
1) структурированность. Должна быть возможность точного поиска по логам, а, возможно, настройки мониторинга по логам. Для этого ключевые события - в частности ошибки - должны однозначно (!) находится по специфической "метке". Пример: " ERROR " при выводе через log4j с форматом по умолчанию. Аналогично нужны "метки" для конкретных типов ошибок, если их нужно отслеживать.
2) достаточная детализация:
а) число записей. Достаточная детализация = логов не слишком мало, чтобы можно было понять причину ошибку, но и не слишком много. Конечно поиск рулит, но лишний объем - это замедление при передаче, хранении и обработке логов.
б) вывод стектрейсов. По умолчанию стектрейсы нужно логировать. Исключения - однозначно локализуемые в коде ошибки или бизнесовые ошибки.
в) поясняющий текст. Данные, содержащиеся в некой переменной, в лог стоит выводить предваряя их поясняющим текстом. Аналогично с текстом пришедшей извне ошибки.
3) корректность:
а) уровень ошибки. ERROR - должен быть ошибкой, прерывающей обработку запроса клиента. Об этом я писал в посте выше
б) отсутствие дублей. Одна ошибка не должна приводить к нескольким стектрейсам в логе. Т.е. выводить в лог исходное исключение, бросать свое, ловить его и снова выводить в лог - это не корректные логи, затрудняющие их обработку.
4) понятный вывод времени. Казалось бы, при чем тут разработчики? Речь о часовом поясе, а это настройки таймзоны для Java. Если время пишется в GMT - нужно постоянно помнить о смещении при разборе логов, а при разборе инцидентов приводит к некорректным выводам.
5) маскирование персональных данных. Если у вас есть ИБ (информационная безопасность) - требования можно спросить у них. Иначе - нагуглить в интернете. Зачем маскировать - излишний вывод (хранение) персональные данных нарушает закон
#logging
Продолжим про логи. Какие требования к ним предъявляются?
Для начала поговорим про разработку:
0) библиотека логирования. Очевидная вещь - для кода, работающего в ПРОМе, логи пишутся только через библиотеку логирования. Т.к. библиотека позволяет настраивать место хранения, формат, фильтрацию логов и многое другое.
1) структурированность. Должна быть возможность точного поиска по логам, а, возможно, настройки мониторинга по логам. Для этого ключевые события - в частности ошибки - должны однозначно (!) находится по специфической "метке". Пример: " ERROR " при выводе через log4j с форматом по умолчанию. Аналогично нужны "метки" для конкретных типов ошибок, если их нужно отслеживать.
2) достаточная детализация:
а) число записей. Достаточная детализация = логов не слишком мало, чтобы можно было понять причину ошибку, но и не слишком много. Конечно поиск рулит, но лишний объем - это замедление при передаче, хранении и обработке логов.
б) вывод стектрейсов. По умолчанию стектрейсы нужно логировать. Исключения - однозначно локализуемые в коде ошибки или бизнесовые ошибки.
в) поясняющий текст. Данные, содержащиеся в некой переменной, в лог стоит выводить предваряя их поясняющим текстом. Аналогично с текстом пришедшей извне ошибки.
3) корректность:
а) уровень ошибки. ERROR - должен быть ошибкой, прерывающей обработку запроса клиента. Об этом я писал в посте выше
б) отсутствие дублей. Одна ошибка не должна приводить к нескольким стектрейсам в логе. Т.е. выводить в лог исходное исключение, бросать свое, ловить его и снова выводить в лог - это не корректные логи, затрудняющие их обработку.
4) понятный вывод времени. Казалось бы, при чем тут разработчики? Речь о часовом поясе, а это настройки таймзоны для Java. Если время пишется в GMT - нужно постоянно помнить о смещении при разборе логов, а при разборе инцидентов приводит к некорректным выводам.
5) маскирование персональных данных. Если у вас есть ИБ (информационная безопасность) - требования можно спросить у них. Иначе - нагуглить в интернете. Зачем маскировать - излишний вывод (хранение) персональные данных нарушает закон
#logging
Всем привет!
Небольшое развитие поста про выбор альтернативы 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
Небольшое развитие поста про выбор альтернативы 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
amplicode.ru
Инструменты разработки веб-приложений | Amplicode
Современные инструменты разработки веб-приложений и сервисов на Spring Boot: надежные инструменты для разработчиков
Всем привет!
Небольшая памятка по созданию бинов в 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
Небольшая памятка по созданию бинов в 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
Stack Overflow
@Bean inside class with @Configuration and without it
There is a @Bean annotation in Spring 3.0. It allows to define a Spring bean directly in a Java code. While browsing Spring reference I found two different ways of using this annotation - inside cl...
Всем привет!
Последняя статья из цикла про журналирование.
Все разработчики сталкиваются с разбором логов. При отладке, в логах сервиса на тестовых средах и ПРОМ, в логах пайплайна сборки или деплоя... Но не все там находят причину проблемы. Перечислю типовые ошибки при разборе. К слову - со всеми из них я сталкивался на практике. Поехали!
1) копать глубже. Найти первую попавшуюся ошибку в логе (или последнюю, смотря с какого края просматривается лог) и успокоится - плохо. Пусть разработчики сервиса, который пишет эти логи, придерживаются правила: ERROR - это всегда ERROR. Все равно - ошибок может быть несколько, ошибки могут выстраиваться в цепочку. Вывод - нужно анализировать весь(все) лог(и).
2) первопричина. Окей - смотрим весь лог, видим цепочку идущих подряд ошибок. Куда смотреть? На первую ошибку по времени. И последнюю в стектрейсе. Это и есть первопричина.
3) ошибки - не всегда ошибки. К сожалению. Встречаются "вечные" ошибки. Или, более приемлемый вариант, постоянные WARNing-и, говорящие о том, что для вашей среды что-то там не поддерживается. И то, и другое нужно игнорировать. Как это понять? Только насмотренность - после того, как этот лог увидишь в десятый (или пятидесятый) раз.
4) случайные ошибки. Если анализировать достаточно большой лог - там с большой вероятностью будут случайные ошибки. "Моргнула" сеть, рестартовал какой-то сервис... Особенно в тестовом контуре, но и на ПРОМе тоже. Снова вопрос - как понять, что ошибку не стоит разбирать? Первое - сравнить количество ошибок разных типов, второе - снова насмотренность.
5) не те логи. В k8s есть логи контейнера сервиса, а есть логи его сайдкаров. Если брать Openshift - для раскатки новой версии DeploymentConfig сервиса в нем используются т.наз. deploy поды, их логи как правило бесполезны. Если брать сервера приложений - там есть логи собственно сервера, а есть - каждого сервиса, развернутого на нем. Ошибка в логах джобы деплоя часто даёт мало информации о причине падения деплоя, нужно смотреть логи контейнеров. Т.е. вначале нужно определится - где может быть ошибка, которую мы ищем. И в конце концов - если в ваших логах есть ошибка интеграции, но причина ее не понятна - стоит попробовать достать логи смежного сервиса.
6) чужая ошибка. Если у нас нагруженная многопользовательская система - в логах могут встречаться ошибки разных пользователей. А мы сейчас разбираем проблему конкретного. Тут поможет фильтрация по атрибуту логов, содержащего id пользователя. Если конечно такой атрибут есть) Спойлер: должен быть)
7) ошибки не то, кем кажутся. Самый сложный кейс - когда сообщение об ошибке не дает полезной информации и даже вводит в заблуждение. Четкого рецепта тут нет - поисковик, ChatGPT, помощь более опытных коллег, просмотр по логам действий, предшествующих ошибке...
#logging
Последняя статья из цикла про журналирование.
Все разработчики сталкиваются с разбором логов. При отладке, в логах сервиса на тестовых средах и ПРОМ, в логах пайплайна сборки или деплоя... Но не все там находят причину проблемы. Перечислю типовые ошибки при разборе. К слову - со всеми из них я сталкивался на практике. Поехали!
1) копать глубже. Найти первую попавшуюся ошибку в логе (или последнюю, смотря с какого края просматривается лог) и успокоится - плохо. Пусть разработчики сервиса, который пишет эти логи, придерживаются правила: ERROR - это всегда ERROR. Все равно - ошибок может быть несколько, ошибки могут выстраиваться в цепочку. Вывод - нужно анализировать весь(все) лог(и).
2) первопричина. Окей - смотрим весь лог, видим цепочку идущих подряд ошибок. Куда смотреть? На первую ошибку по времени. И последнюю в стектрейсе. Это и есть первопричина.
3) ошибки - не всегда ошибки. К сожалению. Встречаются "вечные" ошибки. Или, более приемлемый вариант, постоянные WARNing-и, говорящие о том, что для вашей среды что-то там не поддерживается. И то, и другое нужно игнорировать. Как это понять? Только насмотренность - после того, как этот лог увидишь в десятый (или пятидесятый) раз.
4) случайные ошибки. Если анализировать достаточно большой лог - там с большой вероятностью будут случайные ошибки. "Моргнула" сеть, рестартовал какой-то сервис... Особенно в тестовом контуре, но и на ПРОМе тоже. Снова вопрос - как понять, что ошибку не стоит разбирать? Первое - сравнить количество ошибок разных типов, второе - снова насмотренность.
5) не те логи. В k8s есть логи контейнера сервиса, а есть логи его сайдкаров. Если брать Openshift - для раскатки новой версии DeploymentConfig сервиса в нем используются т.наз. deploy поды, их логи как правило бесполезны. Если брать сервера приложений - там есть логи собственно сервера, а есть - каждого сервиса, развернутого на нем. Ошибка в логах джобы деплоя часто даёт мало информации о причине падения деплоя, нужно смотреть логи контейнеров. Т.е. вначале нужно определится - где может быть ошибка, которую мы ищем. И в конце концов - если в ваших логах есть ошибка интеграции, но причина ее не понятна - стоит попробовать достать логи смежного сервиса.
6) чужая ошибка. Если у нас нагруженная многопользовательская система - в логах могут встречаться ошибки разных пользователей. А мы сейчас разбираем проблему конкретного. Тут поможет фильтрация по атрибуту логов, содержащего id пользователя. Если конечно такой атрибут есть) Спойлер: должен быть)
7) ошибки не то, кем кажутся. Самый сложный кейс - когда сообщение об ошибке не дает полезной информации и даже вводит в заблуждение. Четкого рецепта тут нет - поисковик, ChatGPT, помощь более опытных коллег, просмотр по логам действий, предшествующих ошибке...
#logging
Всем привет!
На этот раз у поста будет заголовок.
7 смертных грехов платформенных компонент.
И сразу небольшое уведомление - данные грехи возможны в любом сервисе, но в случае платформенных компонент они причиняют максимум боли) Ведь в чем суть платформы - не делай свой велосипед, вот тебе готовый, сертифицированный, спроектированный лучшими конструкторами на основе многолетнего опыта... Вы же бизнес-команда, вот и пилите свою бизнес-логику, а платформенные вещи оставьте профессионалам. А по факту ... бывает всякое.
Поехали!
1) слишком тесная связанность. Внешний компонент можно добавить в свой сервис разными способами. Как отдельный под рядом с основным сервисом, как сайдкар, как jar библиотеку. Варианты расположены в порядке увеличения связанности. Больше связанности - это плохо. Почему - отвечу на примере подключаемого jar. Как известно jar не приходит один. А тянет за собой зависимости. Их может быть много, они могут конфликтовать с другими зависимостями, содержать уязвимости, замедлять процесс старта, конфликтовать за общие ресурсы. Процесс подключения может быть нетривиален. А если подключение идет через корневой pom, то мы в каком-то смысле передаем управление нашим сервисом наружу.
2) отдельный пайплайн. Беда не приходит одна, и бывает так что внешний компонент тянет за собой свой CI или CD пайплайн. И то, и другое для конкретного сервиса может быть достаточно сложной и месяцами тщательно настраиваемой штукой. А теперь нам надо встроить туда чужой пайп. Или вызовом, или разобраться что же такого сложного делает внешний пайп и сэмулировать это в своем (а-ля реверс-инжиниринг). Второй вариант - сложно, остановимся на первом. Какие могут быть сложности с внешним пайпом?
а) много непонятных настроек
б) управляет версионированием дистрибутива (снова тесная связанность)
в) делает commits в наш репозиторий
г) требует особых доступов
д) замедляет сборки\установку
е) не дает настраивать манифесты k8s
3) плохая документация. Какой минимум там должен быть:
а) процесс подключения
б) все (!) настройки
в) типовые ошибки и способы их устранения
г) минимальные требования по ресурсам и их изменение в зависимости от нагрузки
д) контакты поддержки
е) канал с информацией об обновлениях
С чем я сталкивался:
а) неочевидные проблемы при сборке\установке. Когда из сообщения об ошибке и документации вообще не ясна причина проблемы
б) неожиданное поведение в runtime, связанное с повышенным потреблением памяти, не описанное четко в документации
в) нет информации о том, в какой версии поддерживается конкретная фича (реализовано требование)
г) нет инструкции по обновлению на новую версию. При этом есть опасение, что поднятием версии процесс не ограничивается
д) магия и ее отсутствие. Поясню. Обещали, что все заработает само. Оно не работает. Почему - не ясно, т.к. принцип работы не описан.
4) имитация поддержки. Она как правило всегда есть, но иногда по факту ее нет) Вот такая диалектика) Хорошо, когда компонента работает из коробки. Как рассказывали на митапе, где она была презентована. А если нет?)
Что я ожидаю:
а) чатик\почту от разработчиков компоненты. лучше чатик
б) таск-трекер и SLA по обработке задач
в) канал с новостями
г) отдельная команда, помогающая с подключением (при необходимости)
д) чатик пользователей для обмена опытом
5) повышенное потребление ресурсов. Компонент закрывает какое нефункциональное требование к системе. Таких требований много. А еще есть бизнес-логика. И выглядит странно, когда один служебный компонент в минимально рекомендуемой конфигурации "жрёт" столько же или больше ресурсов, чем собственно бизнес-сервис.
6) не держит нагрузку. От платформенного "велосипеда" я ожидаю оптимизации в плане производительности. Т.е. он должен держать любую нагрузку, которая может быть в компании, где эта платформа разработана. Не требуя при этом слишком много железа. Иначе зачем это все?)
На этот раз у поста будет заголовок.
7 смертных грехов платформенных компонент.
И сразу небольшое уведомление - данные грехи возможны в любом сервисе, но в случае платформенных компонент они причиняют максимум боли) Ведь в чем суть платформы - не делай свой велосипед, вот тебе готовый, сертифицированный, спроектированный лучшими конструкторами на основе многолетнего опыта... Вы же бизнес-команда, вот и пилите свою бизнес-логику, а платформенные вещи оставьте профессионалам. А по факту ... бывает всякое.
Поехали!
1) слишком тесная связанность. Внешний компонент можно добавить в свой сервис разными способами. Как отдельный под рядом с основным сервисом, как сайдкар, как jar библиотеку. Варианты расположены в порядке увеличения связанности. Больше связанности - это плохо. Почему - отвечу на примере подключаемого jar. Как известно jar не приходит один. А тянет за собой зависимости. Их может быть много, они могут конфликтовать с другими зависимостями, содержать уязвимости, замедлять процесс старта, конфликтовать за общие ресурсы. Процесс подключения может быть нетривиален. А если подключение идет через корневой pom, то мы в каком-то смысле передаем управление нашим сервисом наружу.
2) отдельный пайплайн. Беда не приходит одна, и бывает так что внешний компонент тянет за собой свой CI или CD пайплайн. И то, и другое для конкретного сервиса может быть достаточно сложной и месяцами тщательно настраиваемой штукой. А теперь нам надо встроить туда чужой пайп. Или вызовом, или разобраться что же такого сложного делает внешний пайп и сэмулировать это в своем (а-ля реверс-инжиниринг). Второй вариант - сложно, остановимся на первом. Какие могут быть сложности с внешним пайпом?
а) много непонятных настроек
б) управляет версионированием дистрибутива (снова тесная связанность)
в) делает commits в наш репозиторий
г) требует особых доступов
д) замедляет сборки\установку
е) не дает настраивать манифесты k8s
3) плохая документация. Какой минимум там должен быть:
а) процесс подключения
б) все (!) настройки
в) типовые ошибки и способы их устранения
г) минимальные требования по ресурсам и их изменение в зависимости от нагрузки
д) контакты поддержки
е) канал с информацией об обновлениях
С чем я сталкивался:
а) неочевидные проблемы при сборке\установке. Когда из сообщения об ошибке и документации вообще не ясна причина проблемы
б) неожиданное поведение в runtime, связанное с повышенным потреблением памяти, не описанное четко в документации
в) нет информации о том, в какой версии поддерживается конкретная фича (реализовано требование)
г) нет инструкции по обновлению на новую версию. При этом есть опасение, что поднятием версии процесс не ограничивается
д) магия и ее отсутствие. Поясню. Обещали, что все заработает само. Оно не работает. Почему - не ясно, т.к. принцип работы не описан.
4) имитация поддержки. Она как правило всегда есть, но иногда по факту ее нет) Вот такая диалектика) Хорошо, когда компонента работает из коробки. Как рассказывали на митапе, где она была презентована. А если нет?)
Что я ожидаю:
а) чатик\почту от разработчиков компоненты. лучше чатик
б) таск-трекер и SLA по обработке задач
в) канал с новостями
г) отдельная команда, помогающая с подключением (при необходимости)
д) чатик пользователей для обмена опытом
5) повышенное потребление ресурсов. Компонент закрывает какое нефункциональное требование к системе. Таких требований много. А еще есть бизнес-логика. И выглядит странно, когда один служебный компонент в минимально рекомендуемой конфигурации "жрёт" столько же или больше ресурсов, чем собственно бизнес-сервис.
6) не держит нагрузку. От платформенного "велосипеда" я ожидаю оптимизации в плане производительности. Т.е. он должен держать любую нагрузку, которая может быть в компании, где эта платформа разработана. Не требуя при этом слишком много железа. Иначе зачем это все?)