🐳 Java Testing: Хватит мокать базы данных!
Вы всё еще используете H2 для тестов, а в продакшене стоит PostgreSQL? Или, может быть, вы поднимаете
⛔ Как было раньше
Раньше нам приходилось делать трюки с
✅ Как сейчас
Писать контейнер в каждом тестовом классе - дурной тон (и медленно, так как Docker будет стартовать каждый раз заново).
В Spring Boot 3.1 появился паттерн TestConfiguration, который позволяет переиспользовать контейнеры.
Создаем один файл конфигурации:
Теперь в любом тесте просто импортируем этот конфиг:
Вы можете использовать этот же конфиг, чтобы запустить само приложение локально с поднятыми контейнерами! Больше не нужно настраивать локальный Postgres.
В папке
Запускаете
🏁 Итог
• Изоляция: Каждый прогон тестов - чистая среда.
• Честность: Тестируйте на том, что будет в продакшене.
• Удобство:
#SpringTest #Testcontainers #Java #DevOps #Docker
📲 Мы в MAX
👉 @java_geek
Вы всё еще используете H2 для тестов, а в продакшене стоит PostgreSQL? Или, может быть, вы поднимаете
docker-compose руками перед запуском mvn test?⛔ Как было раньше
Раньше нам приходилось делать трюки с
@DynamicPropertySource, чтобы вручную прокинуть jdbc:url, username и password из поднятого контейнера в контекст Spring.
// Old School (Java 17 / Boot 2.7)
@DynamicPropertySource
static void registerPgProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
// ... куча ручного кода
}
✅ Как сейчас
@ServiceConnection сама понимает, что это за контейнер (Postgres, Redis, Kafka, RabbitMQ), и автоматически прописывает нужные свойства в ваш application.properties на время теста.
@Testcontainers
@SpringBootTest
class OrderIntegrationTest {
// 1. Объявляем контейнер
@Container
@ServiceConnection // 👈 Вся магия тут!
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16-alpine");
@Autowired
private OrderRepository repository;
@Test
void shouldSaveOrder() {
// 2. Тест работает с РЕАЛЬНОЙ базой в Docker
var order = new Order("item-123", BigDecimal.TEN);
repository.save(order);
assertThat(repository.findAll()).hasSize(1);
}
}
Писать контейнер в каждом тестовом классе - дурной тон (и медленно, так как Docker будет стартовать каждый раз заново).
В Spring Boot 3.1 появился паттерн TestConfiguration, который позволяет переиспользовать контейнеры.
Создаем один файл конфигурации:
@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfig {
@Bean
@ServiceConnection
public PostgreSQLContainer<?> postgresContainer() {
return new PostgreSQLContainer<>("postgres:16-alpine");
}
@Bean
@ServiceConnection
public KafkaContainer kafkaContainer() {
return new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.4.0"));
}
}
Теперь в любом тесте просто импортируем этот конфиг:
@Import(ContainersConfig.class)
@SpringBootTest
class AnyIntegrationTest { ... }
Вы можете использовать этот же конфиг, чтобы запустить само приложение локально с поднятыми контейнерами! Больше не нужно настраивать локальный Postgres.
В папке
src/test/java создайте класс для запуска:
public class TestApplication {
public static void main(String[] args) {
SpringApplication.from(MyRealApplication::main)
.with(ContainersConfig.class) // Подключаем наши контейнеры
.run(args);
}
}
Запускаете
TestApplication -> Spring сам поднимает Docker-контейнеры -> приложение коннектится к ним -> вы работаете.🏁 Итог
• Изоляция: Каждый прогон тестов - чистая среда.
• Честность: Тестируйте на том, что будет в продакшене.
• Удобство:
@ServiceConnection убрал весь бойлерплейт.#SpringTest #Testcontainers #Java #DevOps #Docker
📲 Мы в MAX
👉 @java_geek
👍2
🚀 Java на стероидах: Запуск за 0.05 сек с GraalVM Native Image
Главный аргумент хейтеров Java: "Она жирная и медленная на старте".
И они правы. JVM нужно время, чтобы прогреться, загрузить классы и скомпилировать байт-код (JIT). Для микросервисов в Kubernetes или Serverless (AWS Lambda) это критично.
Но GraalVM Native Image превращает ваше Java-приложение в нативный бинарник. Без JVM. Без
🏎 JIT vs AOT: В чем разница?
1. Standard JVM (JIT - Just In Time):
Вы запускаете код -> JVM интерпретирует его -> находит "горячие участки" -> компилирует их в машинный код прямо во время работы.
• Плюс: Максимальная производительность на длинной дистанции (Runtime Optimization).
• Минус: Медленный старт, жрет память на компилятор.
2. Native Image (AOT - Ahead Of Time):
Вы компилируете код заранее. Весь "мертвый" код выкидывается. Остается только то, что реально используется.
• Плюс: Мгновенный старт (< 100ms), мизерное потребление памяти.
• Минус: Долгая сборка (build time), нет оптимизаций во время выполнения.
🛠 Как это выглядит на практике?
В Spring Boot 3 поддержка Native Image встроена официально.
1. Добавляем плагин (Gradle):
2. Собираем:
Внимание: Идите пить кофе. Сборка займет от 2 до 10 минут. GraalVM проводит статический анализ всего вашего кода и всех библиотек.
3. Запускаем:
Да, вы не ослышались. Spring Boot поднимается за 50 миллисекунд.
⚠️ Подводные камни (The Pain Points)
Не всё так радужно. Native Image ненавидит динамику.
1. Reflection API: GraalVM должен знать обо всех классах, которые вы загружаете через рефлексию, на этапе сборки. Раньше нужно было писать тонны JSON-конфигов. Сейчас Spring Boot делает это за вас, но сторонние либы могут сломаться.
2. Resources: Если вы читаете файл из
3. Нет "Write Once, Run Anywhere": Бинарник под Linux не запустится на Windows. Сборку нужно делать в той же OS (или в Docker-контейнере), где будет работать код.
📊 Когда использовать?
✅ Serverless (AWS Lambda, Google Cloud Run): Холодный старт больше не проблема.
✅ CLI утилиты: Консольные тулзы на Java теперь работают мгновенно.
✅ Kubernetes (Scale to Zero): Поды поднимаются и умирают за секунды, экономя ресурсы.
⛔ Обычные микросервисы (High Load): Если сервис живет месяцами и молотит тысячи запросов в секунду, обычная JVM (JIT) обгонит Native Image по пропускной способности (Throughput) за счет рантайм-оптимизаций.
#GraalVM #NativeImage #JavaPerformance #SpringBoot3 #Serverless
📲 Мы в MAX
👉 @java_geek
Главный аргумент хейтеров Java: "Она жирная и медленная на старте".
И они правы. JVM нужно время, чтобы прогреться, загрузить классы и скомпилировать байт-код (JIT). Для микросервисов в Kubernetes или Serverless (AWS Lambda) это критично.
Но GraalVM Native Image превращает ваше Java-приложение в нативный бинарник. Без JVM. Без
java -jar. Просто исполняемый файл.🏎 JIT vs AOT: В чем разница?
1. Standard JVM (JIT - Just In Time):
Вы запускаете код -> JVM интерпретирует его -> находит "горячие участки" -> компилирует их в машинный код прямо во время работы.
• Плюс: Максимальная производительность на длинной дистанции (Runtime Optimization).
• Минус: Медленный старт, жрет память на компилятор.
2. Native Image (AOT - Ahead Of Time):
Вы компилируете код заранее. Весь "мертвый" код выкидывается. Остается только то, что реально используется.
• Плюс: Мгновенный старт (< 100ms), мизерное потребление памяти.
• Минус: Долгая сборка (build time), нет оптимизаций во время выполнения.
🛠 Как это выглядит на практике?
В Spring Boot 3 поддержка Native Image встроена официально.
1. Добавляем плагин (Gradle):
plugins {
id("org.springframework.boot") version "3.2.0"
id("org.graalvm.buildtools.native") version "0.9.28"
}
2. Собираем:
./gradlew nativeCompile
Внимание: Идите пить кофе. Сборка займет от 2 до 10 минут. GraalVM проводит статический анализ всего вашего кода и всех библиотек.
3. Запускаем:
./build/native/nativeCompile/my-app
> Started MyApp in 0.048 seconds (JVM running for 0.052)
Да, вы не ослышались. Spring Boot поднимается за 50 миллисекунд.
⚠️ Подводные камни (The Pain Points)
Не всё так радужно. Native Image ненавидит динамику.
1. Reflection API: GraalVM должен знать обо всех классах, которые вы загружаете через рефлексию, на этапе сборки. Раньше нужно было писать тонны JSON-конфигов. Сейчас Spring Boot делает это за вас, но сторонние либы могут сломаться.
2. Resources: Если вы читаете файл из
src/main/resources не стандартным способом, его может не оказаться в бинарнике.3. Нет "Write Once, Run Anywhere": Бинарник под Linux не запустится на Windows. Сборку нужно делать в той же OS (или в Docker-контейнере), где будет работать код.
📊 Когда использовать?
✅ Serverless (AWS Lambda, Google Cloud Run): Холодный старт больше не проблема.
✅ CLI утилиты: Консольные тулзы на Java теперь работают мгновенно.
✅ Kubernetes (Scale to Zero): Поды поднимаются и умирают за секунды, экономя ресурсы.
⛔ Обычные микросервисы (High Load): Если сервис живет месяцами и молотит тысячи запросов в секунду, обычная JVM (JIT) обгонит Native Image по пропускной способности (Throughput) за счет рантайм-оптимизаций.
#GraalVM #NativeImage #JavaPerformance #SpringBoot3 #Serverless
📲 Мы в MAX
👉 @java_geek
👍1🌚1
🏗 Java: Структурная конкурентность. Прощайте, зомби-потоки!
Допустим, вы используете Виртуальные потоки (Project Loom), чтобы сделать два независимых запроса: получить данные пользователя (API 1) и его заказы (API 2).
Раньше мы использовали
🧟♂️ Проблема зомби-потоков (Unstructured Concurrency)
Если API 1 мгновенно падает с ошибкой 500, ваш метод все равно будет ждать, пока API 2 доработает (или упадет по таймауту). Поток, качающий заказы, становится "сиротой". Он делает бесполезную работу, тратит сеть и память, хотя результат уже никому не нужен.
А если ошибку выкинет родительский метод? Дочерние потоки продолжат жить своей жизнью в фоне. Это хаос.
🧩 Решение:
В современной Java (начиная с 21 версии) потоки привязали к лексической области видимости - блоку кода. Если мы выходим из блока (из-за ошибки или успешного завершения), все запущенные внутри него дочерние потоки автоматически отменяются (получают
Вот как выглядит "Запрос-Ответ", где должны выполниться оба действия (Стратегия *All or Nothing*):
🏎 Стратегия "Кто первый, тот и прав"
А что, если вам нужно получить курс валют, и у вас есть 3 разных провайдера? Вам нужен ответ от любого, кто ответит быстрее.
Как только Банк А ответит, запросы к Банкам B и C будут немедленно отменены. Никакого ручного управления
🧠 Почему это меняет всё?
1. Читаемость: Многопоточный код читается сверху вниз, как обычный синхронный.
2. Безопасность ресурсов: Утечки потоков физически невозможны. Структура гарантирует, что родитель не завершится, пока не разберется со всеми детьми.
3. Идеальные логи: Стек-трейс теперь показывает реальную иерархию (кто кого вызвал), а не обрывается на внутренностях пула потоков.
В связке с Виртуальными потоками это делает Java одним из самых удобных языков для написания высоконагруженных сетевых приложений.
#Concurrency #ProjectLoom #CleanCode #Backend
📲 Мы в MAX
👉 @java_geek
Допустим, вы используете Виртуальные потоки (Project Loom), чтобы сделать два независимых запроса: получить данные пользователя (API 1) и его заказы (API 2).
Раньше мы использовали
ExecutorService или CompletableFuture. Но у них есть огромная архитектурная дыра: они ничего не знают друг о друге.🧟♂️ Проблема зомби-потоков (Unstructured Concurrency)
Если API 1 мгновенно падает с ошибкой 500, ваш метод все равно будет ждать, пока API 2 доработает (или упадет по таймауту). Поток, качающий заказы, становится "сиротой". Он делает бесполезную работу, тратит сеть и память, хотя результат уже никому не нужен.
А если ошибку выкинет родительский метод? Дочерние потоки продолжат жить своей жизнью в фоне. Это хаос.
🧩 Решение:
StructuredTaskScopeВ современной Java (начиная с 21 версии) потоки привязали к лексической области видимости - блоку кода. Если мы выходим из блока (из-за ошибки или успешного завершения), все запущенные внутри него дочерние потоки автоматически отменяются (получают
interrupt).Вот как выглядит "Запрос-Ответ", где должны выполниться оба действия (Стратегия *All or Nothing*):
// ShutdownOnFailure: если один упал, отменяем остальные
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// Запускаем подзадачи (Subtasks)
Subtask<User> user = scope.fork(() -> fetchUser(id));
Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(id));
scope.join(); // Ждем завершения обеих задач...
scope.throwIfFailed(); // ...или первой же ошибки!
// Сюда дойдем, только если обе задачи успешны
return new UserProfile(user.get(), orders.get());
}
// При выходе из блока любые зависшие потоки будут убиты
🏎 Стратегия "Кто первый, тот и прав"
А что, если вам нужно получить курс валют, и у вас есть 3 разных провайдера? Вам нужен ответ от любого, кто ответит быстрее.
// ShutdownOnSuccess: первый успешный отменяет остальные
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<Quote>()) {
scope.fork(() -> getFromBankA());
scope.fork(() -> getFromBankB());
scope.fork(() -> getFromBankC());
scope.join(); // Ждем первого успешного
return scope.result(); // Возвращаем самый быстрый ответ
}
Как только Банк А ответит, запросы к Банкам B и C будут немедленно отменены. Никакого ручного управления
Future.cancel(). Всё работает из коробки.🧠 Почему это меняет всё?
1. Читаемость: Многопоточный код читается сверху вниз, как обычный синхронный.
2. Безопасность ресурсов: Утечки потоков физически невозможны. Структура гарантирует, что родитель не завершится, пока не разберется со всеми детьми.
3. Идеальные логи: Стек-трейс теперь показывает реальную иерархию (кто кого вызвал), а не обрывается на внутренностях пула потоков.
В связке с Виртуальными потоками это делает Java одним из самых удобных языков для написания высоконагруженных сетевых приложений.
#Concurrency #ProjectLoom #CleanCode #Backend
📲 Мы в MAX
👉 @java_geek
👍1🔥1
🚀 Подборка полезных IT каналов в Max
Системное администрирование, DevOps 📌
https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)
Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак
Английский с нуля 🇬🇧
https://max.ru/UchuEnglish
1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://max.ru/channel_DevLab1C 1С:Предприятие 8
Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика
https://max.ru/channel_cpp_geek C++ geek
Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика
Программирование React📌
https://max.ru/react_lib React
Программирование Rust📌
https://max.ru/channel_rust_lib
Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика
Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика
https://max.ru/channel_java_geek Java Geek
GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных
Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков
Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов
Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼💻👩💻
Шутки программистов 📌
https://max.ru/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free
Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров
Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике
Вакансии в IT📌
https://max.ru/progjob
https://max.ru/channel_rabotait
Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных
Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
Системное администрирование, DevOps 📌
https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.
https://max.ru/tipsysdmin Типичный Сисадмин
https://max.ru/channel_win_sysadmin Системный Администратор Windows
https://max.ru/channel_linux_admin Linux: Системный администратор
https://max.ru/channel_linuxmod Linux
https://max.ru/channel_i_linux Системный администратор
https://max.ru/channel_devopslib DevOps, SRE, Sysadmin
https://max.ru/channel_devops_star DevOps Star (Звезда Девопса)
Excel лайфхак 📌
https://t.me/Excel_lifehack Excel лайфхак
Английский с нуля 🇬🇧
https://max.ru/UchuEnglish
1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С
https://max.ru/channel_DevLab1C 1С:Предприятие 8
Программирование C++📌
https://max.ru/cpp_lib Библиотека C/C++ разработчика
https://max.ru/channel_cpp_geek C++ geek
Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика
Программирование React📌
https://max.ru/react_lib React
Программирование Rust📌
https://max.ru/channel_rust_lib
Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика
Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика
https://max.ru/channel_java_geek Java Geek
GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub
Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных
Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков
Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов
Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼💻👩💻
Шутки программистов 📌
https://max.ru/itumor Шутки программистов
Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free
Книги, статьи для дизайнеров 📌
https://max.ru/odesigners Статьи, книги для дизайнеров
Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике
Вакансии в IT📌
https://max.ru/progjob
https://max.ru/channel_rabotait
Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных
Городские📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
https://max.ru/piterspb Питер Новости: Санкт-Петербург / СПБ / ДТП
https://max.ru/channel_krasnodar_novosty Краснодар Новости
https://max.ru/channel_novosibirsk_novosti Новосибирск
https://max.ru/channel_samara_novosti Новости Самары
https://max.ru/channel_ekaterinburg_novosti Новости Екатеринбурга
https://max.ru/channel_kazan_novosti Новости Казани
https://max.ru/channel_omsk_novosti Новости Омска
https://max.ru/channel_moskva_24 Москва 24
MAX
Системный Администратор | Sysadmin Windows & Linux Server. …
Купить рекламу: https://telega.in/m/i_odmin
Блог практикующего админа. Настройка Windows Server, Active Directory (AD), GPO и терминальных серверов (RDP). Раб…
Блог практикующего админа. Настройка Windows Server, Active Directory (AD), GPO и терминальных серверов (RDP). Раб…
🏗 System Design: Эволюция архитектуры от 1 до 1 000 000 пользователей
Главная ошибка разработчиков при проектировании систем - строить звездолет для поездки за хлебом. Микросервисы, Kafka и Kubernetes не нужны вашему стартапу в первый день.
Архитектура должна эволюционировать шаг за шагом. Вот как выглядит этот путь.
Уровень 1: Одинокий Волк (1 - 1000 юзеров)
Всё крутится на одном сервере (например, в DigitalOcean или AWS EC2).
• Что там: Ваше Java-приложение (Monolith) + база данных (PostgreSQL) + веб-сервер (Nginx) живут на одной машине.
• Плюсы: Развертывание занимает 5 минут, всё работает быстро (сетевые задержки нулевые).
• Минусы: Если сервер упал - упало всё. Масштабировать можно только покупкой более мощного процессора/памяти (Вертикальное масштабирование).
Уровень 2: Разделение труда (10 000 юзеров)
Приложение начинает тормозить, потому что СУБД "съела" всю оперативную память.
• Что делаем: Выносим базу данных на отдельный сервер. Желательно использовать управляемое решение (Managed DB от облачного провайдера), чтобы не возиться с бэкапами.
• Результат: Приложение и БД больше не дерутся за ресурсы.
Уровень 3: Горизонтальное масштабирование (100 000 юзеров)
Трафик растет. Один сервер приложения больше не справляется с HTTP-запросами.
• Что делаем: Ставим Load Balancer (Балансировщик нагрузки) и поднимаем 3-5 одинаковых серверов с вашим Java-приложением.
• Правило: Ваше приложение должно стать Stateless (без состояния). Вы больше не можете хранить сессии пользователей в локальной памяти (RAM), иначе юзер залогинится на Сервере 1, а следующий запрос попадет на Сервер 2, и его "выкинет". Сессии уезжают в централизованное хранилище.
Уровень 4: Спасаем базу данных (500 000 юзеров)
Приложений много, а БД одна. Она начинает "задыхаться" от количества чтений.
• Что делаем (Кэш): Ставим Redis или Memcached. До 80% запросов в типичном приложении - это чтение одних и тех же данных. Кэш отдает их за миллисекунды.
• Что делаем (Репликация): Разделяем БД на Master (для записи) и несколько Slave/Replica (только для чтения).
Уровень 5: Асинхронность и Очереди (1 000 000+ юзеров)
Пользователи жалуются, что загрузка отчета или обработка видео занимает слишком много времени, а HTTP-соединения отваливаются по таймауту.
• Что делаем: Внедряем брокер сообщений (Kafka или RabbitMQ) и создаем воркеры.
• Как это работает: Юзер жмет "сгенерировать отчет". Приложение кидает задачу в Kafka и мгновенно отвечает юзеру: "В процессе". А фоновые серверы-воркеры не спеша забирают задачи из очереди и делают тяжелую работу.
🧠 Главный принцип System Design
Не усложняйте систему до тех пор, пока метрики не покажут, что текущий уровень больше не справляется. Каждое усложнение (Load Balancer, Redis, Kafka) несет за собой новые проблемы: инвалидация кэша, задержки сети, дублирование сообщений.
#SystemDesign #Architecture #Backend #Scaling
📲 Мы в MAX
👉 @java_geek
Главная ошибка разработчиков при проектировании систем - строить звездолет для поездки за хлебом. Микросервисы, Kafka и Kubernetes не нужны вашему стартапу в первый день.
Архитектура должна эволюционировать шаг за шагом. Вот как выглядит этот путь.
Уровень 1: Одинокий Волк (1 - 1000 юзеров)
Всё крутится на одном сервере (например, в DigitalOcean или AWS EC2).
• Что там: Ваше Java-приложение (Monolith) + база данных (PostgreSQL) + веб-сервер (Nginx) живут на одной машине.
• Плюсы: Развертывание занимает 5 минут, всё работает быстро (сетевые задержки нулевые).
• Минусы: Если сервер упал - упало всё. Масштабировать можно только покупкой более мощного процессора/памяти (Вертикальное масштабирование).
Уровень 2: Разделение труда (10 000 юзеров)
Приложение начинает тормозить, потому что СУБД "съела" всю оперативную память.
• Что делаем: Выносим базу данных на отдельный сервер. Желательно использовать управляемое решение (Managed DB от облачного провайдера), чтобы не возиться с бэкапами.
• Результат: Приложение и БД больше не дерутся за ресурсы.
Уровень 3: Горизонтальное масштабирование (100 000 юзеров)
Трафик растет. Один сервер приложения больше не справляется с HTTP-запросами.
• Что делаем: Ставим Load Balancer (Балансировщик нагрузки) и поднимаем 3-5 одинаковых серверов с вашим Java-приложением.
• Правило: Ваше приложение должно стать Stateless (без состояния). Вы больше не можете хранить сессии пользователей в локальной памяти (RAM), иначе юзер залогинится на Сервере 1, а следующий запрос попадет на Сервер 2, и его "выкинет". Сессии уезжают в централизованное хранилище.
Уровень 4: Спасаем базу данных (500 000 юзеров)
Приложений много, а БД одна. Она начинает "задыхаться" от количества чтений.
• Что делаем (Кэш): Ставим Redis или Memcached. До 80% запросов в типичном приложении - это чтение одних и тех же данных. Кэш отдает их за миллисекунды.
• Что делаем (Репликация): Разделяем БД на Master (для записи) и несколько Slave/Replica (только для чтения).
Уровень 5: Асинхронность и Очереди (1 000 000+ юзеров)
Пользователи жалуются, что загрузка отчета или обработка видео занимает слишком много времени, а HTTP-соединения отваливаются по таймауту.
• Что делаем: Внедряем брокер сообщений (Kafka или RabbitMQ) и создаем воркеры.
• Как это работает: Юзер жмет "сгенерировать отчет". Приложение кидает задачу в Kafka и мгновенно отвечает юзеру: "В процессе". А фоновые серверы-воркеры не спеша забирают задачи из очереди и делают тяжелую работу.
🧠 Главный принцип System Design
Не усложняйте систему до тех пор, пока метрики не покажут, что текущий уровень больше не справляется. Каждое усложнение (Load Balancer, Redis, Kafka) несет за собой новые проблемы: инвалидация кэша, задержки сети, дублирование сообщений.
#SystemDesign #Architecture #Backend #Scaling
📲 Мы в MAX
👉 @java_geek
👍2🔥2❤1
В чем отличия между TreeSet и HashSet?
Основное отличие между
📲 Мы в MAX
👉 @java_geek
Основное отличие между
TreeSet и HashSet заключается в том, что TreeSet хранит элементы в отсортированном порядке, в то время как порядок хранения элементов в HashSet не определен. TreeSet также обеспечивает быстрый поиск и извлечение элементов в отсортированном порядке.📲 Мы в MAX
👉 @java_geek
👍2
Fluent Interface
Fluent Interface (Текучий интерфейс) — это шаблон проектирования, который позволяет создавать код, читающийся как текст на естественном языке.
Он достигается путем создания методов, которые возвращают ссылку на this объект, позволяя вызывать их в цепочке.
📲 Мы в MAX
👉 @java_geek
Fluent Interface (Текучий интерфейс) — это шаблон проектирования, который позволяет создавать код, читающийся как текст на естественном языке.
Он достигается путем создания методов, которые возвращают ссылку на this объект, позволяя вызывать их в цепочке.
📲 Мы в MAX
👉 @java_geek
Что такое перегрузка методов в Java?
Перегрузка методов — это мощный приём, который позволяет классу иметь несколько методов с одинаковым именем, но разными параметрами. Это даёт объектам класса возможность обрабатывать близкие (почти одинаковые) задачи, но с разными входными данными.
Вот очень короткий пример, демонстрирующий перегрузку методов:
В этом примере класс
один принимает два целых числа (
а другой — два числа с плавающей запятой (
Имя метода одинаковое, но параметры различаются, что позволяет вызывать подходящий метод в зависимости от типов аргументов.
📲 Мы в MAX
👉 @java_geek
Перегрузка методов — это мощный приём, который позволяет классу иметь несколько методов с одинаковым именем, но разными параметрами. Это даёт объектам класса возможность обрабатывать близкие (почти одинаковые) задачи, но с разными входными данными.
Вот очень короткий пример, демонстрирующий перегрузку методов:
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
В этом примере класс
Calculator имеет два метода add:один принимает два целых числа (
int) и возвращает сумму в виде целого числа,а другой — два числа с плавающей запятой (
double) и возвращает сумму в виде double.Имя метода одинаковое, но параметры различаются, что позволяет вызывать подходящий метод в зависимости от типов аргументов.
📲 Мы в MAX
👉 @java_geek
👍1
☕ Java Tip: Как работает
С версии Java 10 появился ключевое слово
✅ Удобно: меньше шаблонного кода.
❌ Важно:
📌 Используй
📲 Мы в MAX
👉 @java_geek
var в JavaС версии Java 10 появился ключевое слово
var. Оно упрощает код, автоматически выводя тип переменной:
var list = new ArrayList<String>();
list.add("Hello");
list.add("Java");
// Компилятор понимает, что list -> ArrayList<String>
✅ Удобно: меньше шаблонного кода.
❌ Важно:
var не значит "динамическая типизация"! Тип фиксируется при компиляции.📌 Используй
var, если тип переменной очевиден - код станет чище.📲 Мы в MAX
👉 @java_geek
👍1👎1🔥1
Какова цель ключевого слова final, когда оно используется с переменной?
Ключевое слово final в Java — это модификатор, который может применяться к переменным, методам и классам. Когда оно используется с переменной, это делает её неизменяемой, другими словами — константой. Например, переменная PI объявлена как final в классе ниже:
📲 Мы в MAX
👉 @java_geek
Ключевое слово final в Java — это модификатор, который может применяться к переменным, методам и классам. Когда оно используется с переменной, это делает её неизменяемой, другими словами — константой. Например, переменная PI объявлена как final в классе ниже:
public class CircleCalculator {
private final double PI = 3.14159;
public double calculateArea(double radius) {
return PI * radius * radius;
}
}
📲 Мы в MAX
👉 @java_geek
👍1
Array vs ArrayList
Выбор между Array (стандартным Java-массивом) и ArrayList зависит от специфики задачи на Java, которую требуется решить. Помните о следующих особенностях этих типов:
☕️ Array имеет фиксированный размер и память для него выделяется в момент объявления, а размер ArrayList может динамически изменяться;
☕️ Массивы Java работают гораздо быстрее, а в ArrayList намного проще добавлять/удалять элементы;
☕️ При работе с Array велика вероятность получить ошибку ArrayIndexOutOfBoundsException;
☕️ У ArrayList только одно измерение, а вот массивы Java могут быть многомерными.
📲 Мы в MAX
👉 @java_geek
Выбор между Array (стандартным Java-массивом) и ArrayList зависит от специфики задачи на Java, которую требуется решить. Помните о следующих особенностях этих типов:
☕️ Array имеет фиксированный размер и память для него выделяется в момент объявления, а размер ArrayList может динамически изменяться;
☕️ Массивы Java работают гораздо быстрее, а в ArrayList намного проще добавлять/удалять элементы;
☕️ При работе с Array велика вероятность получить ошибку ArrayIndexOutOfBoundsException;
☕️ У ArrayList только одно измерение, а вот массивы Java могут быть многомерными.
📲 Мы в MAX
👉 @java_geek
👍1
Как перебрать элементы LinkedList в обратном порядке, не используя медленный get(index)?
Для этого в LinkedList есть обратный итератор, который можно получить, вызвав метод
📲 Мы в MAX
👉 @java_geek
Для этого в LinkedList есть обратный итератор, который можно получить, вызвав метод
descendingIterator().📲 Мы в MAX
👉 @java_geek
👍2
Что такое стек-трейс?
Стек-трейс (stack trace) представляет собой список вызовов методов в обратном хронологическом порядке, начиная с метода, в котором произошло исключение. Стек-трейс позволяет отследить, какие методы были вызваны перед возникновением исключения, и предоставляет информацию о местоположении, где произошло исключение.
📲 Мы в MAX
👉 @java_geek
Стек-трейс (stack trace) представляет собой список вызовов методов в обратном хронологическом порядке, начиная с метода, в котором произошло исключение. Стек-трейс позволяет отследить, какие методы были вызваны перед возникновением исключения, и предоставляет информацию о местоположении, где произошло исключение.
📲 Мы в MAX
👉 @java_geek
👍1