Разработка программного обеспечения. Меня зовут Евгений Сулейманов. Я инженер по разработке ПО. Специализация - высоконагруженные распределённые системы. Публикую обучающие видеоматериалы по языкам программирования, фреймворкам, базам данных, облачным сервисам и т.д.
Спасибо за видео! Кратко, ёмко и суперполезно! Сразу тяжело всё понять - перематываю, пересматриваю, стараюсь разобраться. Столкнулся с таким моментом: в классе SingleThreadPoolDemo компилятор ругается на блок try-with-resources, говорит, что ExecutorService не является AutoCloseable. В итоге выяснилось, что начиная с Java 21, ExecutorService был расширен, чтобы имплементировать интерфейс AutoCloseable, а я использовал старую версию Джавы. Поэтому, чтобы корректно завершить работу ExecutorService в старых версиях, нужно явно вызвать его методы shutdown() и awaitTermination(). Вот такой вариант работает для старых версий Джавы: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class SingleThreadPoolDemo { public static void main(String[] args) { long start = System.currentTimeMillis(); ExecutorService executorService = Executors.newSingleThreadExecutor(); try { for (int i = 0; i < 100; i++) { executorService.submit(new GenerateRandomIntegerTask()); } } finally { executorService.shutdown(); // Корректное завершение работы потока try { // Ждём завершения всех задач, но не более 1 минуты if (!executorService.awaitTermination(1, TimeUnit.MINUTES)) { executorService.shutdownNow(); // Принудительное завершение при таймауте } } catch (InterruptedException e) { executorService.shutdownNow(); // Принудительное завершение при ошибке } long end = System.currentTimeMillis(); long duration = end - start; System.out.println("Processed in: " + duration + " ms"); } } }
Да и выбор кубернетеса - очень плохое решение. Посмотрите на ваши нагрузки, ни один кластер не выдержит нагузку, да и сеть не выдержит такую нагрузку. Тут как минимум нужны десятки кластеров в каждой зоне доступности, + нужны кеширующие сервера на разных уровнях сети
В explain 1 цифра - это стоимость для начала конкретного этапа, а 2 - это общая стоимость выполнения. Первая цифра включает все операции выполненные до этого, чтобы начать текущий этап. Чтобы получить стоимость конкретного этапа нужно вычесть из цифры 2 цифру 1. Общая стоимость выполнения запроса это 2 цифра на последнем этапе, цифры не нужно суммировать, как это делает автор. По сути после первого преобразования запроса автором, запрос отработал медленнее))) Но это вполне объяснимо маленьким объемом данных и поэтому цифры будут скакать)
Раз даже через три года автор не удосужился рассказать, зачем нужен новый уровень абстракции, попробую я - представьте что для создания экземпляров каждого девелопера нужно тащить кучу сервисов, дао, конфигов из контекста (dependency injection) вот и получается, что ваш класс раздулся до огромных размеров. Поэтому хорошобы спрятать логику создания в конкретные фабрики, а потом унифицированно и компактно вызывать их методы
Спасибо за видео! Помню работал не на спринговом проекте, написал заявление на увольнение и последние 2 недели не сделал практически ни одной задачи, очень сильно перегорел. На тот момент уже был оффер на проект с новым стеком. Эти две недели отрабатывал дома, позволял себе заниматься не работой, а своими делами (например смотрел про спринг илм другие технологии которые будут на новой работе). Я думал ну все равно уволюсь, и лучше будет если подготовлюсь к новой работе. Все таки двухнедельная отработка это тоже работа, за которую я получаю деньги. И я не имею права забивать на это. Считаю это поступком дилетанта.
спасибо! подскажите, а действительно нужно тестировать базовые методы типа save, findAll или saveAll? это было показано для примера, разве в реальном коде не тестируют только какую то кастомную логику, кастомные запросы?
@@EugeneSuleimanov спасибо за ответ! подскажите еще пжлст, а можно проверять конкретно sql запросы при тестировании repository? например, проверить отсутствие проблемы N+1, или хибернейт может разные алиасы каждый раз делать и такое невозможно протестить?
Добрый день, Евгений. Спасибо за Ваш полезный труд! Повторял код за Вами и уперся в докеризацию. Контейнер создал, но при его подъеме ловлю исключение - SQL State : 08001 2024-08-20T10:13:02.695640490Z Error Code : 0 2024-08-20T10:13:02.695641735Z Message : Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. При этом локально БД работает. гугл и chat Gpt пока не дали ответ( Может, есть альтернатива - extra_hosts: - "host.docker.internal:host-gateway"? Спасибо за помощь!
спасибо за видео! Евгений, вы на работе используете docker desktop или используете консольные команды? что предподчительнее? Например у вас в команде может есть особые случаи. Но я точно знаю что на серваке только командами можно использовать )
На всякий случай: автор вводит в заблуждение по поводу недостатков NoSQL баз данных. В частности NoSQL MongoDb нормально поддерживает транзакции, имеют хорошие возможности отладки и отлично устойчива к любым внешним сбоям. А перечисленные достоинства SQL можно так же отнести и к некоторым NoSQL базам данных. Поэтому нужно аккуратно доверять некоторым оценкам автора.
@@Serg-q6y спасибо за комментарий! Буду признателен за ссылки на документацию. Если под поддержкой транзакций (ACID) в Mongo мы говорим о CAS и ACID на УРОВНЕ ДОКУМЕНТА, то я остаюсь при своем мнении.
Спасибо! Отличный полнометражный блокбастер. Но при поднятии контейнера country-api падает исключение java.net.UnknownHostException: host.docker.internal на Linux Mint 21.2 может кто-то знает как решить?
Евгений, спасибо за курс! Очень основательно и детально, впрочем, как всегда. У меня остался вопрос по докерфайлу -- у вас не возникает проблем с тестами при использовании testcontainers при такой конфигурации, когда сначала билд тул импортируется, а затем он запускает билд? Получается, gradle сам находится в контейнере, и у него нет доступа к контейнеру ryuk:testcontainers, т.к. последний является внешним по отношению к gradle. Я себя на проекте я отказался от докерфайла, и собираю образ при помощи gradle плагина -- при этом я перенастроил процесс сборки, чтобы тесты выполнялись до собирания образа. По мне, это является костылём, и возможно вы знаете более оптимальный подход, при котором можно продолжать использовать dockerfile?
Спасибо большое за видео, Евгений! Много нового узнал, хоть уже с многопоточностью довольно давно знаком. Было очень интересно. Хотелось уточнить, что в примере с "first", "second", "third" можно было бы поочередно запускать, потом дожидаться выполнения каждого из потоков и в таком случае бы было поочередно выведены "first", "second" и "third". Я понимаю что в этом варианте не было бы никакого смысла от создания нескольких потоков, и все можно было бы последовательно выводить в одном, но думаю для полноты картины будет полезно знать, если вдруг кто-то задастся вопросом "а почему не сделать t1.start(); t1.join(); t2.start...?"
Пример с Callable и Future не совсем корректный, из-за того, что executor Service создается в try с ресурсами и будет закрыт после выхода из try, мы вынуждены ждать, пока все потоки не закончат работу. На видео это не заметно, так все выполняется большим количеством потоков, но если поставить выполнять это все 2-3 потока, то к брейкпоинту на строчке 22 программа будет идти очень долго