Простая установка Nginx на Ubuntu
Установка Nginx на Ubuntu проста и занимает всего несколько минут.
Обновите списки пакетов:
Установите Nginx:
Запустите Nginx:
Включите автозапуск Nginx:
Проверьте статус Nginx:
Для настройки брандмауэра (если используется ufw) разрешите HTTP-трафик:
Демонстрация работы Nginx
После выполнения вышеуказанных шагов откройте веб-браузер и введите IP-адрес вашего сервера (например, http://your_server_ip). Вы увидите стандартную страницу приветствия Nginx.
Эта страница подтверждает, что Nginx установлен и функционирует корректно. Если страница не отображается, проверьте статус сервера и настройки брандмауэра.
#Java #middle #on_request #nginx
Установка Nginx на Ubuntu проста и занимает всего несколько минут.
Обновите списки пакетов:
sudo apt update
Эта команда обновляет индекс пакетов для системы управления пакетами apt.
Установите Nginx:
sudo apt install nginx
Подтвердите установку, нажав Y и Enter, когда система запросит разрешение.
Запустите Nginx:
sudo systemctl start nginx
Эта команда запускает веб-сервер.
Включите автозапуск Nginx:
sudo systemctl enable nginx
Это гарантирует, что Nginx будет запускаться автоматически при перезагрузке системы.
Проверьте статус Nginx:
sudo systemctl status nginx
Если в выводе указано active (running), сервер работает корректно.
Для настройки брандмауэра (если используется ufw) разрешите HTTP-трафик:
sudo ufw allow 'Nginx HTTP'
sudo ufw status
Демонстрация работы Nginx
После выполнения вышеуказанных шагов откройте веб-браузер и введите IP-адрес вашего сервера (например, http://your_server_ip). Вы увидите стандартную страницу приветствия Nginx.
Эта страница подтверждает, что Nginx установлен и функционирует корректно. Если страница не отображается, проверьте статус сервера и настройки брандмауэра.
#Java #middle #on_request #nginx
👍5🔥1
Раздел 4: Управляющие конструкции
Глава 2: Циклы
while / do-while в Java
Циклы в Java позволяют выполнять блок кода несколько раз, что полезно для повторяющихся задач, таких как обработка данных или ожидание ввода пользователя. В этом уроке мы разберем два типа циклов: while и do-while. Они используются, когда количество итераций неизвестно заранее и зависит от условия.
1. Цикл while
1.1. Синтаксис
Цикл while проверяет условие перед каждой итерацией. Если условие истинно (true), код внутри цикла выполняется. Если ложно (false), выполнение переходит к следующей строке после цикла.
1.2. Как работает
Проверяется условие.
Если условие истинно, выполняется блок кода.
После выполнения блока кода условие проверяется снова.
Если условие ложно, цикл завершается, и выполнение продолжается после цикла.
1.3. Примеры
Простой счетчик
Суммирование чисел до ввода 0
Цикл с несколькими условиями
Бесконечный цикл
1.4. Особенности
Проверка условия: Условие проверяется перед выполнением блока кода, поэтому цикл может не выполниться ни разу, если условие изначально ложно.
Обновление переменных: Необходимо обновлять переменные внутри цикла, чтобы условие в конечном итоге стало ложным.
Использование: Подходит для задач, где количество итераций неизвестно, например, чтение данных до конца файла или ожидание ввода пользователя.
2. Цикл do-while
2.1. Синтаксис
Цикл do-while выполняет блок кода хотя бы один раз, а затем проверяет условие. Если условие истинно, цикл продолжается.
2.2. Как работает
Выполняется блок кода.
Проверяется условие.
Если условие истинно, выполнение возвращается к блоку кода.
Если условие ложно, цикл завершается.
#Java #для_новичков #beginner #while #do_while
Глава 2: Циклы
while / do-while в Java
Циклы в Java позволяют выполнять блок кода несколько раз, что полезно для повторяющихся задач, таких как обработка данных или ожидание ввода пользователя. В этом уроке мы разберем два типа циклов: while и do-while. Они используются, когда количество итераций неизвестно заранее и зависит от условия.
1. Цикл while
1.1. Синтаксис
Цикл while проверяет условие перед каждой итерацией. Если условие истинно (true), код внутри цикла выполняется. Если ложно (false), выполнение переходит к следующей строке после цикла.
while (условие) {
// Код, который выполняется, если условие истинно
}
Условие: Выражение, возвращающее boolean (true или false).
Блок кода: Выполняется, пока условие истинно.
1.2. Как работает
Проверяется условие.
Если условие истинно, выполняется блок кода.
После выполнения блока кода условие проверяется снова.
Если условие ложно, цикл завершается, и выполнение продолжается после цикла.
1.3. Примеры
Простой счетчик
int i = 1;
while (i <= 5) {
System.out.println("Число: " + i);
i++;
}
Вывод:
Число: 1
Число: 2
Число: 3
Число: 4
Число: 5
Объяснение: Цикл начинается с i = 1. Пока i <= 5, печатается значение i, и i увеличивается на 1. Когда i становится 6, условие становится ложным, и цикл завершается.
Суммирование чисел до ввода 0
import java.util.Scanner;
Scanner scanner = new Scanner(System.in);
int sum = 0;
int number;
System.out.println("Введите числа для суммирования (0 для завершения):");
number = scanner.nextInt();
while (number != 0) {
sum += number;
number = scanner.nextInt();
}
System.out.println("Сумма: " + sum);
Объяснение: Пользователь вводит числа, которые добавляются к sum, пока не введет 0. Цикл while проверяет number != 0 перед добавлением.
Цикл с несколькими условиями
int x = 10;
int limit = 20;
while (x > 0 && x < limit) {
System.out.println("x = " + x);
x--;
}
Вывод:
x = 10
x = 9
x = 8
...
x = 1
Объяснение: Цикл выполняется, пока выполняются оба условия: x > 0 и x < limit. Числа печатаются от 10 до 1.
Бесконечный цикл
int i = 0;
while (true) {
System.out.println("Итерация: " + i);
i++;
if (i >= 3) {
break; // Выход из цикла
}
}
Вывод:
Итерация: 0
Итерация: 1
Итерация: 2
Объяснение: Условие true делает цикл бесконечным, но break прерывает его, когда i достигает 3.
1.4. Особенности
Проверка условия: Условие проверяется перед выполнением блока кода, поэтому цикл может не выполниться ни разу, если условие изначально ложно.
Обновление переменных: Необходимо обновлять переменные внутри цикла, чтобы условие в конечном итоге стало ложным.
Использование: Подходит для задач, где количество итераций неизвестно, например, чтение данных до конца файла или ожидание ввода пользователя.
2. Цикл do-while
2.1. Синтаксис
Цикл do-while выполняет блок кода хотя бы один раз, а затем проверяет условие. Если условие истинно, цикл продолжается.
do {
// Код, который выполняется хотя бы один раз
} while (условие);
Условие: Выражение, возвращающее boolean.
Блок кода: Выполняется перед проверкой условия.
2.2. Как работает
Выполняется блок кода.
Проверяется условие.
Если условие истинно, выполнение возвращается к блоку кода.
Если условие ложно, цикл завершается.
#Java #для_новичков #beginner #while #do_while
👍4
2.3. Примеры
Простой счетчик
Выполнение при ложном условии
Меню с вводом пользователя
2.4. Особенности
Гарантированное выполнение: Блок кода выполняется хотя бы один раз, даже если условие ложно.
Использование: Подходит для задач, где нужно выполнить действие хотя бы раз, например, отображение меню или запрос ввода.
3. Правильное применение
3.1. Лучшие практики
Используйте фигурные скобки {}:
Обновляйте переменные:
Убедитесь, что переменные в условии изменяются, чтобы избежать бесконечных циклов.
Проверяйте на null:
Используйте понятные имена:
Например, counter вместо i делает код понятнее.
Избегайте бесконечных циклов:
3.2. Распространенные ошибки
Неправильный выбор цикла:
Использование do-while, когда while лучше, или наоборот.
Сложные условия:
4. Работа под капотом
4.1. Компиляция в байт-код
Цикл while:
Компилируется в инструкции if и goto. Условие проверяется перед выполнением, и если оно истинно, JVM переходит к блоку кода.
Пример байт-кода (упрощенно):
Цикл do-while:
Блок кода выполняется сначала, затем проверяется условие с помощью if и goto.
Пример байт-кода:
4.2. Память и стек
Стек операндов: Условие цикла вычисляется в стеке операндов JVM.
Стек вызовов: Локальные переменные цикла (например, i) хранятся в стеке вызовов.
Куча: Если в цикле создаются объекты (например, new String()), они хранятся в куче.
4.3. Оптимизация в JVM
JIT-компиляция: JVM может оптимизировать циклы, встраивая их в машинный код для повышения производительности.
Короткое замыкание: Если условие содержит логические операторы (&&, ||), JVM пропускает ненужные вычисления.
Удаление пустых циклов: Если цикл не выполняет полезной работы, JIT-компилятор может его убрать.
4.4. Ошибки в памяти
Бесконечные циклы: Могут переполнить стек или кучу, если создаются объекты.
NullPointerException: Работа с объектами без проверки на null в условии.
#Java #для_новичков #beginner #while #do_while
Простой счетчик
int i = 1;
do {
System.out.println("Число: " + i);
i++;
} while (i <= 5);
Вывод:
Число: 1
Число: 2
Число: 3
Число: 4
Число: 5
Объяснение: Блок кода выполняется, затем проверяется i <= 5. Цикл продолжается, пока условие истинно.
Выполнение при ложном условии
int i = 6;
do {
System.out.println("Это выполнится один раз");
} while (i <= 5);
Вывод: Это выполнится один раз
Объяснение: Блок кода выполняется один раз, даже если условие i <= 5 изначально ложно.
Меню с вводом пользователя
import java.util.Scanner;
Scanner scanner = new Scanner(System.in);
String input;
do {
System.out.print("Введите команду (или 'quit' для выхода): ");
input = scanner.nextLine();
System.out.println("Вы ввели: " + input);
} while (!input.equalsIgnoreCase("quit"));
Объяснение: Пользователь вводит команды, пока не введет "quit". Цикл гарантирует, что запрос появится хотя бы один раз.
2.4. Особенности
Гарантированное выполнение: Блок кода выполняется хотя бы один раз, даже если условие ложно.
Использование: Подходит для задач, где нужно выполнить действие хотя бы раз, например, отображение меню или запрос ввода.
3. Правильное применение
3.1. Лучшие практики
Используйте фигурные скобки {}:
Даже для одной строки, чтобы избежать ошибок и улучшить читаемость.// Плохо: без скобок
while (i < 5)
System.out.println(i++);
// Хорошо: со скобками
while (i < 5) {
System.out.println(i++);
}
Обновляйте переменные:
Убедитесь, что переменные в условии изменяются, чтобы избежать бесконечных циклов.
Проверяйте на null:
Если работаете с объектами, проверяйте их на null, чтобы избежать NullPointerException.String input = null;
while (input != null && !input.isEmpty()) {
// Обработка ввода
}
Используйте понятные имена:
Например, counter вместо i делает код понятнее.
Избегайте бесконечных циклов:
Если используете while (true), добавьте break для выхода.while (true) {
if (условие) break;
}
3.2. Распространенные ошибки
Забыть обновить переменную:int i = 1;
while (i <= 5) {
System.out.println(i); // Бесконечный цикл, так как i не увеличивается
}
Неправильный выбор цикла:
Использование do-while, когда while лучше, или наоборот.
Сложные условия:
Слишком сложные условия затрудняют чтение. Разбивайте их на переменные.
// Плохо
while (x > 0 && y < 10 && z != null && z.isValid()) {}
// Хорошо
boolean isValid = x > 0 && y < 10 && z != null && z.isValid();
while (isValid) {}
4. Работа под капотом
4.1. Компиляция в байт-код
Цикл while:
Компилируется в инструкции if и goto. Условие проверяется перед выполнением, и если оно истинно, JVM переходит к блоку кода.
Пример байт-кода (упрощенно):
while (i < 5) {
i++;
}
Байт-код:iload i
bipush 5
if_icmpge end
iinc i, 1
goto loop
end:
Цикл do-while:
Блок кода выполняется сначала, затем проверяется условие с помощью if и goto.
Пример байт-кода:
do {
i++;
} while (i < 5);
Байт-код:loop:
iinc i, 1
iload i
bipush 5
if_icmplt loop
4.2. Память и стек
Стек операндов: Условие цикла вычисляется в стеке операндов JVM.
Стек вызовов: Локальные переменные цикла (например, i) хранятся в стеке вызовов.
Куча: Если в цикле создаются объекты (например, new String()), они хранятся в куче.
4.3. Оптимизация в JVM
JIT-компиляция: JVM может оптимизировать циклы, встраивая их в машинный код для повышения производительности.
Короткое замыкание: Если условие содержит логические операторы (&&, ||), JVM пропускает ненужные вычисления.
Удаление пустых циклов: Если цикл не выполняет полезной работы, JIT-компилятор может его убрать.
4.4. Ошибки в памяти
Бесконечные циклы: Могут переполнить стек или кучу, если создаются объекты.
NullPointerException: Работа с объектами без проверки на null в условии.
String s = null;
while (s.length() > 0) { // Ошибка: NullPointerException
}
#Java #для_новичков #beginner #while #do_while
👍3
Apache Kafka.
Введение и архитектура
Apache Kafka представляет собой распределенную платформу для обработки потоков данных в реальном времени, которая сочетает в себе функции очереди сообщений, хранилища данных и системы обработки событий. Разработанная изначально в LinkedIn для решения задач высоконагруженных систем, Kafka эволюционировала в мощный инструмент для построения масштабируемых конвейеров данных.
Основные концепции: topic, partition, offset, segment, log, leader/follower, ISR
Kafka строится вокруг понятия Topic — логическая категория для потоков сообщений.
Topic — это не монолитная структура, а распределенная очередь, разделенная на партиции (partitions). Каждая партиция представляет собой упорядоченную, неизменяемую последовательность записей (records), которая хранится как append-only лог. Это значит, что данные в партиции добавляются только в конец, без возможности модификации существующих записей. Партиции позволяют параллелизовать обработку: разные партиции могут обрабатываться независимо, что обеспечивает масштабируемость.
Внутри партиции каждая запись идентифицируется offset'ом — это монотонно возрастающее целое число, начиная с 0, которое указывает позицию записи в логе. Offset уникален только в пределах партиции; для разных партиций offset'ы независимы. Когда потребитель (consumer) читает данные, он отслеживает текущий offset, чтобы знать, с какой позиции продолжить чтение. В памяти потребителя offset хранится локально, но для надежности Kafka предоставляет механизм коммита offset'ов в специальную внутреннюю тему
Партиция физически хранится как лог (log) — последовательность файлов на диске брокера. Лог разбивается на сегменты (segments) для управления размером: каждый сегмент — это файл с записями, начиная с определенного offset'а (base offset). Когда сегмент достигает заданного размера (по умолчанию 1 ГБ, создается новый. Старые сегменты могут удаляться по политикам retention. В памяти брокера сегменты не загружаются целиком; вместо этого Kafka использует memory-mapped files (mmap) для доступа к диску, что позволяет ОС кэшировать горячие данные в page cache, минимизируя реальные I/O-операции.
Для репликации партиции имеют лидера (leader) и фолловеров (followers). Лидер — это реплика партиции на одном брокере, которая принимает все записи от продюсеров (producers) и обслуживает чтение от потребителей. Фолловеры — реплики на других брокерах, которые синхронизируют данные с лидером. ISR (In-Sync Replicas) — это подмножество реплик (включая лидера), которые полностью синхронизированы с лидером. ISR определяется по отставанию фолловеров: если фолловер не запрашивает данные в течение
В памяти брокера для каждой партиции лидер хранит в RAM метаданные, такие как текущий high-watermark (максимальный offset, закоммиченный на всех ISR), а также буферы для входящих запросов. Фолловеры используют отдельные потоки (replica fetchers) для pull-запросов к лидеру, копируя данные в свои логи.
Архитектура брокера: роль брокера, контроллера, брокерная конфигурация
Брокер (broker) — это основной узел (сервер) Kafka-кластера, отвечающий за хранение и обслуживание данных. Каждый брокер управляет подмножеством партиций: для каждой партиции один брокер является лидером, а другие — фолловерами. Брокеры образуют кластер, координируемый через ZooKeeper (до Kafka 2.8) или встроенный KRaft (Kafka Raft) в новых версиях, который устраняет зависимость от ZooKeeper.
#Java #middle #Kafka
Введение и архитектура
Apache Kafka представляет собой распределенную платформу для обработки потоков данных в реальном времени, которая сочетает в себе функции очереди сообщений, хранилища данных и системы обработки событий. Разработанная изначально в LinkedIn для решения задач высоконагруженных систем, Kafka эволюционировала в мощный инструмент для построения масштабируемых конвейеров данных.
Основные концепции: topic, partition, offset, segment, log, leader/follower, ISR
Kafka строится вокруг понятия Topic — логическая категория для потоков сообщений.
Topic — это не монолитная структура, а распределенная очередь, разделенная на партиции (partitions). Каждая партиция представляет собой упорядоченную, неизменяемую последовательность записей (records), которая хранится как append-only лог. Это значит, что данные в партиции добавляются только в конец, без возможности модификации существующих записей. Партиции позволяют параллелизовать обработку: разные партиции могут обрабатываться независимо, что обеспечивает масштабируемость.
Внутри партиции каждая запись идентифицируется offset'ом — это монотонно возрастающее целое число, начиная с 0, которое указывает позицию записи в логе. Offset уникален только в пределах партиции; для разных партиций offset'ы независимы. Когда потребитель (consumer) читает данные, он отслеживает текущий offset, чтобы знать, с какой позиции продолжить чтение. В памяти потребителя offset хранится локально, но для надежности Kafka предоставляет механизм коммита offset'ов в специальную внутреннюю тему
__consumer_offsets
, где они реплицируются как обычные записи.Партиция физически хранится как лог (log) — последовательность файлов на диске брокера. Лог разбивается на сегменты (segments) для управления размером: каждый сегмент — это файл с записями, начиная с определенного offset'а (base offset). Когда сегмент достигает заданного размера (по умолчанию 1 ГБ, создается новый. Старые сегменты могут удаляться по политикам retention. В памяти брокера сегменты не загружаются целиком; вместо этого Kafka использует memory-mapped files (mmap) для доступа к диску, что позволяет ОС кэшировать горячие данные в page cache, минимизируя реальные I/O-операции.
Для репликации партиции имеют лидера (leader) и фолловеров (followers). Лидер — это реплика партиции на одном брокере, которая принимает все записи от продюсеров (producers) и обслуживает чтение от потребителей. Фолловеры — реплики на других брокерах, которые синхронизируют данные с лидером. ISR (In-Sync Replicas) — это подмножество реплик (включая лидера), которые полностью синхронизированы с лидером. ISR определяется по отставанию фолловеров: если фолловер не запрашивает данные в течение
replica.lag.time.max.ms
(по умолчанию 30 секунд), он исключается из ISR. Это обеспечивает баланс между доступностью и consistency: записи считаются закоммиченными, когда они реплицированы на все реплики в ISR (min.insync.replicas
).В памяти брокера для каждой партиции лидер хранит в RAM метаданные, такие как текущий high-watermark (максимальный offset, закоммиченный на всех ISR), а также буферы для входящих запросов. Фолловеры используют отдельные потоки (replica fetchers) для pull-запросов к лидеру, копируя данные в свои логи.
Архитектура брокера: роль брокера, контроллера, брокерная конфигурация
Брокер (broker) — это основной узел (сервер) Kafka-кластера, отвечающий за хранение и обслуживание данных. Каждый брокер управляет подмножеством партиций: для каждой партиции один брокер является лидером, а другие — фолловерами. Брокеры образуют кластер, координируемый через ZooKeeper (до Kafka 2.8) или встроенный KRaft (Kafka Raft) в новых версиях, который устраняет зависимость от ZooKeeper.
#Java #middle #Kafka
👍6
Контроллер (controller) — это специальный брокер, избираемый кластером для управления метаданными: распределение партиций, лидер-элекшн, обработка изменений в топиках. Контроллер мониторит состояние брокеров через heartbeat'ы и перераспределяет партиции при сбоях. В памяти контроллера хранится глобальное состояние кластера: mapping партиций к брокерам, ISR для каждой партиции. При сбое контроллера избирается новый, что занимает миллисекунды благодаря репликации метаданных в
Брокерная конфигурация определяет поведение:
В памяти брокера значительная часть heap'а (до 50% по умолчанию) выделяется под off-heap буферы для сетевых операций, чтобы избежать GC-пауз. Конфигурация влияет на производительность: слишком малое
Хранение данных: лог-сегменты, индексные файлы, retention vs compaction
Данные в партиции хранятся в лог-сегментах: каждый сегмент состоит из двух файлов —
Запись в
Retention — это политика удаления старых данных:
Trade-offs: retention подходит для временных данных (например, логи), но тратит диск на устаревшие записи; compaction экономит место для stateful данных (например, конфиги), но увеличивает CPU/IO на cleanup. Влияние
Репликация: лидеры, ISR, replica fetcher, лидер-элекшн
Репликация обеспечивает надежность: каждая партиция имеет
Лидер-элекшн происходит при сбое лидера: контроллер выбирает нового лидера из ISR (предпочтительно unclean.leader.election.enable=false, чтобы избежать data loss). Элекшн использует epoch для предотвращения split-brain: новый лидер увеличивает epoch, уведомляя фолловеров. В памяти брокера реплика хранит log end offset (LEO) — максимальный offset в логе, и high-watermark.
#Java #middle #Kafka
__controller_epoch
теме.Брокерная конфигурация определяет поведение:
broker.id
— уникальный ID, log.dirs
— директории для логов, num.network.threads
— количество потоков для сетевых запросов (по умолчанию 3), num.io.threads
— для дисковых операций (по умолчанию 8). В памяти брокера значительная часть heap'а (до 50% по умолчанию) выделяется под off-heap буферы для сетевых операций, чтобы избежать GC-пауз. Конфигурация влияет на производительность: слишком малое
num.io.threads
может привести к bottleneck'у на диске, а большое message.max.bytes
увеличивает потребление памяти для батчинга.Хранение данных: лог-сегменты, индексные файлы, retention vs compaction
Данные в партиции хранятся в лог-сегментах: каждый сегмент состоит из двух файлов —
.log
(сами записи) и .index
(спарс-индекс для быстрого поиска по offset'у). Запись в
.log
— это последовательность байт: заголовок (magic byte, attributes, timestamp), ключ, значение, headers. Индексный файл содержит пары (offset, position), где position — байтовое смещение в .log
. Индекс спарсный (по умолчанию каждые 4 КБ, configurable via index.interval.bytes
), чтобы минимизировать размер: поиск offset'а начинается с ближайшей индексной записи, за которой следует линейный скан.Retention — это политика удаления старых данных:
log.retention.hours
(по умолчанию 168) удаляет сегменты по времени, log.retention.bytes
— по размеру. Compaction — альтернатива для key-based тем: сохраняет только последнюю запись для каждого ключа, удаляя дубликаты. Compaction работает в фоне: cleaner thread сканирует сегменты, строит в памяти map ключей к последним offset'ам, затем перезаписывает сегмент. В памяти это требует heap'а пропорционально количеству уникальных ключей в сегменте.Trade-offs: retention подходит для временных данных (например, логи), но тратит диск на устаревшие записи; compaction экономит место для stateful данных (например, конфиги), но увеличивает CPU/IO на cleanup. Влияние
segment.bytes
: маленькие сегменты (например, 100 МБ) ускоряют deletion/compaction, снижая GC (меньше объектов в heap во время cleanup), но увеличивают overhead на открытые файлы и индексы. Большие сегменты минимизируют фрагментацию, но замедляют GC/IO при compaction, так как требуют больше памяти для временных структур.Репликация: лидеры, ISR, replica fetcher, лидер-элекшн
Репликация обеспечивает надежность: каждая партиция имеет
replication.factor
(по умолчанию 1-3) реплик. Продюсеры пишут только в лидера, который реплицирует данные в ISR. Replica fetcher — это поток на фолловере, который периодически посылает FetchRequest к лидеру, запрашивая данные с последнего fetched offset'а. Лидер проверяет, в ISR ли фолловер, и отправляет данные. High-watermark продвигается только когда все ISR зареплицировали запись, обеспечивая durability.Лидер-элекшн происходит при сбое лидера: контроллер выбирает нового лидера из ISR (предпочтительно unclean.leader.election.enable=false, чтобы избежать data loss). Элекшн использует epoch для предотвращения split-brain: новый лидер увеличивает epoch, уведомляя фолловеров. В памяти брокера реплика хранит log end offset (LEO) — максимальный offset в логе, и high-watermark.
#Java #middle #Kafka
👍6
Сетевая модель: request/response, metadata, fetch/produce
Kafka использует асинхронный, бинарный протокол на TCP: клиенты посылают requests (ProduceRequest, FetchRequest), брокеры отвечают responses. MetadataRequest запрашивает топик-метаданные (партиции, лидеры) от любого брокера, который перенаправляет к контроллеру если нужно. Produce — для записи: продюсер батчит записи по партициям, посылая в лидера. Fetch — для чтения: потребитель запрашивает с offset'а, получая chunk данных.
В памяти: брокер использует NIO selectors для multiplexing соединений, буферы (ByteBuffer) для zero-copy передачи. Zero-copy с sendfile() позволяет передавать данные из page cache напрямую в socket, без копирования в user space.
Обзор API-моделей
- Producer API: Для отправки записей. Создает ProducerRecord (topic, partition, key, value), использует KafkaProducer с конфигами (acks=all для durability, batch.size для батчинга).
- Consumer API: Для чтения. KafkaConsumer с poll(), который возвращает ConsumerRecords. Поддерживает группы для балансировки партиций.
- Admin API: Для управления топиками (create, delete, describe).
- Streams API: Для обработки потоков (KStream, KTable) с state stores.
- Connect API: Для интеграции с внешними системами (sources/sinks).
Ordering guarantees, масштабирование vs ordering
Kafka гарантирует ordering только внутри партиции: записи с одним ключом (если key-based partitioning) идут в порядке отправки. Нет глобального ordering по топику. Масштабирование добавлением партиций улучшает throughput, но жертвует ordering: для строгого ordering используйте одну партицию, что лимитирует parallelism.
Почему Kafka быстрая
- Sequential I/O: Запись/чтение в append-only лог — последовательные операции на диске, эффективные для HDD/SSD (миллионы IOPS vs random access).
- Zero-copy: Sendfile() копирует данные из kernel cache в socket без user space, снижая CPU и latency.
- Batching: Продюсеры/потребители батчат записи (linger.ms), амортизируя overhead сети/диска. В памяти батчи сжимаются (compression.type).
Минимальный producer/consumer
Producer (Java):
Consumer (Java):
#Java #middle #Kafka
Kafka использует асинхронный, бинарный протокол на TCP: клиенты посылают requests (ProduceRequest, FetchRequest), брокеры отвечают responses. MetadataRequest запрашивает топик-метаданные (партиции, лидеры) от любого брокера, который перенаправляет к контроллеру если нужно. Produce — для записи: продюсер батчит записи по партициям, посылая в лидера. Fetch — для чтения: потребитель запрашивает с offset'а, получая chunk данных.
В памяти: брокер использует NIO selectors для multiplexing соединений, буферы (ByteBuffer) для zero-copy передачи. Zero-copy с sendfile() позволяет передавать данные из page cache напрямую в socket, без копирования в user space.
Обзор API-моделей
- Producer API: Для отправки записей. Создает ProducerRecord (topic, partition, key, value), использует KafkaProducer с конфигами (acks=all для durability, batch.size для батчинга).
- Consumer API: Для чтения. KafkaConsumer с poll(), который возвращает ConsumerRecords. Поддерживает группы для балансировки партиций.
- Admin API: Для управления топиками (create, delete, describe).
- Streams API: Для обработки потоков (KStream, KTable) с state stores.
- Connect API: Для интеграции с внешними системами (sources/sinks).
Ordering guarantees, масштабирование vs ordering
Kafka гарантирует ordering только внутри партиции: записи с одним ключом (если key-based partitioning) идут в порядке отправки. Нет глобального ordering по топику. Масштабирование добавлением партиций улучшает throughput, но жертвует ordering: для строгого ordering используйте одну партицию, что лимитирует parallelism.
Почему Kafka быстрая
- Sequential I/O: Запись/чтение в append-only лог — последовательные операции на диске, эффективные для HDD/SSD (миллионы IOPS vs random access).
- Zero-copy: Sendfile() копирует данные из kernel cache в socket без user space, снижая CPU и latency.
- Batching: Продюсеры/потребители батчат записи (linger.ms), амортизируя overhead сети/диска. В памяти батчи сжимаются (compression.type).
Минимальный producer/consumer
Producer (Java):
import org.apache.kafka.clients.producer.*;
import java.util.Properties;
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
ProducerRecord<String, String> record = new ProducerRecord<>("my-topic", "key", "value");
producer.send(record);
producer.close();
Consumer (Java):
import org.apache.kafka.clients.consumer.*;
import java.util.Properties;
import java.util.Collections;
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "my-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singleton("my-topic"));
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> record : records) {
System.out.println(record.value());
}
consumer.close();
#Java #middle #Kafka
🔥3👍2
Break, continue, метки (label) в Java
В циклах иногда нужно прервать выполнение или пропустить часть кода. Для этого в Java используются операторы break и continue. Метки (labels) позволяют управлять вложенными циклами. Эти инструменты помогают делать циклы гибкими и эффективными.
Что такое break, continue и метки в Java?
- break: Полностью прерывает цикл и выходит из него. Полезно, когда условие для продолжения больше не нужно.
- continue: Пропускает остаток текущей итерации (повторения) цикла и переходит к следующей. Полезно, чтобы игнорировать некоторые случаи.
- Метки (labels): Это специальные имена перед циклами, которые позволяют break или continue влиять на внешние циклы в вложенных конструкциях.
Зачем нужны эти операторы?
- Управление потоком: Позволяют досрочно завершать или пропускать части цикла.
- Эффективность: Избегают ненужных повторений, делая код быстрее.
- Читаемость: Делают логику цикла понятной, особенно в сложных случаях.
- Гибкость: Метки помогают работать с вложенными циклами, как в обработке таблиц или массивов.
Синтаксис
break
- Прерывает ближайший цикл или switch и выходит из него.
Синтаксис:
С меткой (для вложенных циклов):
continue
- Пропускает остаток текущей итерации и переходит к следующей проверке условия.
Синтаксис:
С меткой:
Метки (labels)
- Метка — это имя с двоеточием (:) перед циклом или блоком.
Синтаксис:
- Метки должны быть уникальными и состоять из букв, цифр или подчеркиваний, как переменные.
Примечания к синтаксису:
- break и continue работают в циклах (for, while, do-while) и switch.
- Без метки они влияют на ближайший цикл.
- Метки используются редко, но полезны в сложных вложенных циклах.
Примеры использования
break в цикле
- Прерывает цикл, когда число больше 3.
Вывод:
- Объяснение: Когда i становится 4, if истинно, break прерывает цикл, и 5 не печатается.
continue в цикле
- Пропускает четные числа.
Вывод:
- Объяснение: Если i четное, continue пропускает печать и переходит к следующей итерации.
Метки с break
- Вложенные циклы: прерывает внешний цикл.
Вывод:
- Объяснение: Когда j == 2, break outer прерывает весь внешний цикл.
Метки с continue
- Пропускает итерацию внешнего цикла.
Вывод:
- Объяснение: Когда j == 2, continue outer пропускает остаток итерации внешнего цикла и переходит к следующей i.
Правильное применение
break
- Используйте, когда нужно досрочно выйти из цикла (например, поиск элемента в списке).
- Пример: Поиск числа в массиве.
continue
- Используйте, чтобы пропустить ненужные случаи (например, игнорировать пустые строки).
- Пример: Суммирование только положительных чисел.
#Java #для_новичков #beginner #break #continue
В циклах иногда нужно прервать выполнение или пропустить часть кода. Для этого в Java используются операторы break и continue. Метки (labels) позволяют управлять вложенными циклами. Эти инструменты помогают делать циклы гибкими и эффективными.
Что такое break, continue и метки в Java?
- break: Полностью прерывает цикл и выходит из него. Полезно, когда условие для продолжения больше не нужно.
- continue: Пропускает остаток текущей итерации (повторения) цикла и переходит к следующей. Полезно, чтобы игнорировать некоторые случаи.
- Метки (labels): Это специальные имена перед циклами, которые позволяют break или continue влиять на внешние циклы в вложенных конструкциях.
Зачем нужны эти операторы?
- Управление потоком: Позволяют досрочно завершать или пропускать части цикла.
- Эффективность: Избегают ненужных повторений, делая код быстрее.
- Читаемость: Делают логику цикла понятной, особенно в сложных случаях.
- Гибкость: Метки помогают работать с вложенными циклами, как в обработке таблиц или массивов.
Синтаксис
break
- Прерывает ближайший цикл или switch и выходит из него.
Синтаксис:
break;
С меткой (для вложенных циклов):
метка: {
// Цикл
break метка;
}
continue
- Пропускает остаток текущей итерации и переходит к следующей проверке условия.
Синтаксис:
continue;
С меткой:
метка: {
// Цикл
continue метка;
}
Метки (labels)
- Метка — это имя с двоеточием (:) перед циклом или блоком.
Синтаксис:
имяМетки: for (...) {
// Код
break имяМетки; // или continue имяМетки
}
- Метки должны быть уникальными и состоять из букв, цифр или подчеркиваний, как переменные.
Примечания к синтаксису:
- break и continue работают в циклах (for, while, do-while) и switch.
- Без метки они влияют на ближайший цикл.
- Метки используются редко, но полезны в сложных вложенных циклах.
Примеры использования
break в цикле
- Прерывает цикл, когда число больше 3.
int i = 1;
while (i <= 5) {
System.out.println(i);
if (i > 3) {
break;
}
i++;
}
Вывод:
1
2
3
4
- Объяснение: Когда i становится 4, if истинно, break прерывает цикл, и 5 не печатается.
continue в цикле
- Пропускает четные числа.
for (int i = 1; i <= 5; i++) {
if (i % 2 == 0) {
continue;
}
System.out.println(i);
}
Вывод:
1
3
5
- Объяснение: Если i четное, continue пропускает печать и переходит к следующей итерации.
Метки с break
- Вложенные циклы: прерывает внешний цикл.
outer: for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) {
break outer; // Прерывает внешний цикл
}
System.out.println("i=" + i + ", j=" + j);
}
}
Вывод:
i=1, j=1
- Объяснение: Когда j == 2, break outer прерывает весь внешний цикл.
Метки с continue
- Пропускает итерацию внешнего цикла.
outer: for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
if (j == 2) {
continue outer; // Переходит к следующей итерации внешнего цикла
}
System.out.println("i=" + i + ", j=" + j);
}
}
Вывод:
i=1, j=1
i=2, j=1
i=3, j=1
- Объяснение: Когда j == 2, continue outer пропускает остаток итерации внешнего цикла и переходит к следующей i.
Правильное применение
break
- Используйте, когда нужно досрочно выйти из цикла (например, поиск элемента в списке).
- Пример: Поиск числа в массиве.
int[] numbers = {1, 2, 3, 4, 5};
int target = 3;
boolean found = false;
for (int num : numbers) {
if (num == target) {
found = true;
break;
}
}
System.out.println("Найдено: " + found);
continue
- Используйте, чтобы пропустить ненужные случаи (например, игнорировать пустые строки).
- Пример: Суммирование только положительных чисел.
int sum = 0;
for (int i = -2; i <= 3; i++) {
if (i <= 0) {
continue;
}
sum += i;
}
System.out.println("Сумма: " + sum); // 1 + 2 + 3 = 6
#Java #для_новичков #beginner #break #continue
👍4
Правильное применение
break
- Используйте, когда нужно досрочно выйти из цикла (например, поиск элемента в списке).
- Пример: Поиск числа в массиве.
continue
- Используйте, чтобы пропустить ненужные случаи (например, игнорировать пустые строки).
- Пример: Суммирование только положительных чисел.
Метки
- Используйте в вложенных циклах, когда нужно влиять на внешний цикл.
- Пример: Поиск в матрице.
Рекомендации
- Избегайте меток, если возможно — используйте методы для упрощения кода.
- Документируйте метки комментариями, так как они делают код сложнее.
- Тестируйте циклы на бесконечность или пропуски.
Работа под капотом
Компиляция в байт-код
- break: Компилируется в инструкцию goto для перехода к концу цикла.
- continue: Компилируется в goto для возврата к началу цикла (проверке условия).
- Метки: Метка становится меткой в байт-коде, а break/continue с меткой — goto к этой метке.
Пример break в цикле:
Байт-код (упрощенно):
Память и стек
- break и continue не влияют напрямую на память, но прерывают или пропускают код, экономя ресурсы.
- Вложенные циклы с метками используют стек вызовов для локальных переменных.
Оптимизация в JVM
- JIT-компилятор может встраивать циклы с break/continue, оптимизируя переходы.
- В бесконечных циклах с break JVM может оптимизировать, если видит частый выход.
Ошибки в памяти
- Бесконечные циклы без break могут переполнить стек или кучу.
- Неправильные метки могут привести к неожиданным переходам.
#Java #для_новичков #beginner #break #continue
break
- Используйте, когда нужно досрочно выйти из цикла (например, поиск элемента в списке).
- Пример: Поиск числа в массиве.
int[] numbers = {1, 2, 3, 4, 5};
int target = 3;
boolean found = false;
for (int num : numbers) {
if (num == target) {
found = true;
break;
}
}
System.out.println("Найдено: " + found);
continue
- Используйте, чтобы пропустить ненужные случаи (например, игнорировать пустые строки).
- Пример: Суммирование только положительных чисел.
int sum = 0;
for (int i = -2; i <= 3; i++) {
if (i <= 0) {
continue;
}
sum += i;
}
System.out.println("Сумма: " + sum); // 1 + 2 + 3 = 6
Метки
- Используйте в вложенных циклах, когда нужно влиять на внешний цикл.
- Пример: Поиск в матрице.
int[][] matrix = {{1, 2}, {3, 4}};
outer: for (int row = 0; row < matrix.length; row++) {
for (int col = 0; col < matrix[row].length; col++) {
if (matrix[row][col] == 3) {
System.out.println("Найдено в строке " + row + ", столбце " + col);
break outer;
}
}
}
Рекомендации
- Избегайте меток, если возможно — используйте методы для упрощения кода.
- Документируйте метки комментариями, так как они делают код сложнее.
- Тестируйте циклы на бесконечность или пропуски.
Работа под капотом
Компиляция в байт-код
- break: Компилируется в инструкцию goto для перехода к концу цикла.
- continue: Компилируется в goto для возврата к началу цикла (проверке условия).
- Метки: Метка становится меткой в байт-коде, а break/continue с меткой — goto к этой метке.
Пример break в цикле:
while (true) {
if (condition) break;
}
Байт-код (упрощенно):
loop:
if condition goto end
goto loop
end:
Память и стек
- break и continue не влияют напрямую на память, но прерывают или пропускают код, экономя ресурсы.
- Вложенные циклы с метками используют стек вызовов для локальных переменных.
Оптимизация в JVM
- JIT-компилятор может встраивать циклы с break/continue, оптимизируя переходы.
- В бесконечных циклах с break JVM может оптимизировать, если видит частый выход.
Ошибки в памяти
- Бесконечные циклы без break могут переполнить стек или кучу.
- Неправильные метки могут привести к неожиданным переходам.
#Java #для_новичков #beginner #break #continue
👍5
Apache Kafka
Producer — гарантии, производительность, транзакции
Apache Kafka Producer — это клиентская компонента, ответственная за отправку сообщений в Kafka-кластер.
Основные конфиги: acks, linger.ms, batch.size, compression.type, buffer.memory
Продюсер настраивается через свойства в Properties объекте (в Java/Scala) или аналогичные в других клиентах. Эти конфиги определяют баланс между latency, throughput и durability.
- acks: Определяет уровень подтверждения от брокера. Возможные значения: 0 (fire-and-forget, нет подтверждения, минимальная latency, но возможна потеря данных при сбое), 1 (подтверждение от лидера, данные записаны на диск лидера, но не реплицированы), all (или -1, подтверждение после репликации на все ISR, максимальная durability). В памяти продюсера: при acks=all запрос блокируется до получения подтверждения от min.insync.replicas брокеров. Это влияет на throughput: acks=0 дает миллионы сообщений/сек, но без гарантий; acks=all снижает до сотен тысяч из-за ожидания репликации. Нюанс: при acks=all, если ISR сокращается (например, из-за лага фолловеров), продюсер может бросить MetadataException, требуя ручного вмешательства.
- linger.ms: Время ожидания перед отправкой батча (по умолчанию 0). Продюсер аккумулирует сообщения в памяти (в RecordAccumulator), группируя по партициям. Если батч не заполнен в течение linger.ms, он отправляется принудительно. Это trade-off: высокое значение (например, 5-10 мс) увеличивает размер батча, амортизируя network overhead, но повышает latency. В памяти: таймер на основе ScheduledExecutorService проверяет батчи; overhead — минимальный, но при высоком linger.ms память может заполняться, если трафик низкий.
- batch.size: Максимальный размер батча в байтах (по умолчанию 16 КБ). Когда батч достигает этого размера, он отправляется немедленно, игнорируя linger.ms. В памяти: каждый батч — это Deque<ProducerBatch> в RecordAccumulator, где ProducerBatch содержит ByteBuffer для сериализованных записей. Нюанс: слишком большой batch.size (например, 1 МБ) увеличивает память (buffer.memory), но снижает I/O на брокере; малый — приводит к частым мелким запросам, увеличивая CPU на serialization и network.
- compression.type: Тип сжатия (none, gzip, snappy, lz4, zstd). Сжатие происходит в памяти продюсера перед добавлением в батч: сериализованные записи компрессируются в ByteBuffer. Это снижает объем данных на сети/диске, но добавляет CPU overhead. Подробнее в senior-нюансах ниже.
- buffer.memory: Общий размер буфера в памяти для несент батчей (по умолчанию 32 МБ). Это off-heap память (DirectByteBuffer), чтобы избежать GC. Если буфер заполнен, send() блокируется (configurable via max.block.ms). Нюанс: при высоком трафике и медленной сети буфер может переполниться, вызывая ProducerFencedException в транзакционных режимах; мониторьте метрики bufferpool-usage.
В памяти продюсера: основной компонент — RecordAccumulator, который держит Map<TopicPartition, Deque<ProducerBatch>>. Каждое send() сериализует ProducerRecord, вычисляет партицию (via Partitioner), добавляет в батч. Отдельный Sender thread (в KafkaProducer) периодически проверяет батчи и отправляет ProduceRequest через NetworkClient (NIO-based).
#Java #middle #Kafka #Produser
Producer — гарантии, производительность, транзакции
Apache Kafka Producer — это клиентская компонента, ответственная за отправку сообщений в Kafka-кластер.
Основные конфиги: acks, linger.ms, batch.size, compression.type, buffer.memory
Продюсер настраивается через свойства в Properties объекте (в Java/Scala) или аналогичные в других клиентах. Эти конфиги определяют баланс между latency, throughput и durability.
- acks: Определяет уровень подтверждения от брокера. Возможные значения: 0 (fire-and-forget, нет подтверждения, минимальная latency, но возможна потеря данных при сбое), 1 (подтверждение от лидера, данные записаны на диск лидера, но не реплицированы), all (или -1, подтверждение после репликации на все ISR, максимальная durability). В памяти продюсера: при acks=all запрос блокируется до получения подтверждения от min.insync.replicas брокеров. Это влияет на throughput: acks=0 дает миллионы сообщений/сек, но без гарантий; acks=all снижает до сотен тысяч из-за ожидания репликации. Нюанс: при acks=all, если ISR сокращается (например, из-за лага фолловеров), продюсер может бросить MetadataException, требуя ручного вмешательства.
- linger.ms: Время ожидания перед отправкой батча (по умолчанию 0). Продюсер аккумулирует сообщения в памяти (в RecordAccumulator), группируя по партициям. Если батч не заполнен в течение linger.ms, он отправляется принудительно. Это trade-off: высокое значение (например, 5-10 мс) увеличивает размер батча, амортизируя network overhead, но повышает latency. В памяти: таймер на основе ScheduledExecutorService проверяет батчи; overhead — минимальный, но при высоком linger.ms память может заполняться, если трафик низкий.
- batch.size: Максимальный размер батча в байтах (по умолчанию 16 КБ). Когда батч достигает этого размера, он отправляется немедленно, игнорируя linger.ms. В памяти: каждый батч — это Deque<ProducerBatch> в RecordAccumulator, где ProducerBatch содержит ByteBuffer для сериализованных записей. Нюанс: слишком большой batch.size (например, 1 МБ) увеличивает память (buffer.memory), но снижает I/O на брокере; малый — приводит к частым мелким запросам, увеличивая CPU на serialization и network.
- compression.type: Тип сжатия (none, gzip, snappy, lz4, zstd). Сжатие происходит в памяти продюсера перед добавлением в батч: сериализованные записи компрессируются в ByteBuffer. Это снижает объем данных на сети/диске, но добавляет CPU overhead. Подробнее в senior-нюансах ниже.
- buffer.memory: Общий размер буфера в памяти для несент батчей (по умолчанию 32 МБ). Это off-heap память (DirectByteBuffer), чтобы избежать GC. Если буфер заполнен, send() блокируется (configurable via max.block.ms). Нюанс: при высоком трафике и медленной сети буфер может переполниться, вызывая ProducerFencedException в транзакционных режимах; мониторьте метрики bufferpool-usage.
В памяти продюсера: основной компонент — RecordAccumulator, который держит Map<TopicPartition, Deque<ProducerBatch>>. Каждое send() сериализует ProducerRecord, вычисляет партицию (via Partitioner), добавляет в батч. Отдельный Sender thread (в KafkaProducer) периодически проверяет батчи и отправляет ProduceRequest через NetworkClient (NIO-based).
#Java #middle #Kafka #Produser
👍3
Idempotence: enable.idempotence=true, гарантии без дублей
Idempotent producer обеспечивает exactly-once семантику без дубликатов при ретреях. Включается via enable.idempotence=true, что implicitly устанавливает acks=all, retries>0, max.in.flight.requests.per.connection=5 (или меньше).
Как работает: каждому продюсеру присваивается уникальный producer.id (PID) от брокера при init. Каждому батчу добавляется sequence number (начиная с 0 per partition). Брокер хранит last sequence per PID/partition и отвергает дубликаты (если sequence уже обработан). При ретрее продюсер переотправляет с тем же sequence.
В памяти: продюсер держит Map<TopicPartition, Integer> для sequences. Overhead: минимальный, но требует стабильного соединения (если connection drops, может потребоваться reinitialization). Гарантии: at-least-once becomes exactly-once для одной сессии; не покрывает множественные продюсеры или app restarts (для этого — transactions).
Нюанс: idempotence не влияет на ordering, если max.in.flight=1; но по умолчанию позволяет до 5 in-flight, что может нарушить order (см. senior-нюансы).
Transactions (EOS): transactional.id, initTransactions(), ограничения и caveats
Transactions предоставляют exactly-once semantics (EOS) через топики, включая atomicity: все или ничего для группы send(). Включается via transactional.id (уникальный ID, persistent across restarts).
Процесс: producer.initTransactions() регистрирует PID с брокером (via InitProducerIdRequest), устанавливая epoch. Затем beginTransaction(), send()..., commitTransaction() или abortTransaction(). В commit брокер пишет маркеры (commit/abort) в логи, делая транзакцию видимой для потребителей с isolation.level=read_committed.
В памяти: продюсер держит TransactionManager, который тракает открытые транзакции, pending batches. Batches помечаются transactionally; overhead — дополнительные метаданные в ProduceRequest.
Ограничения: только для idempotent producers; max.in.flight=1 (принудительно, чтобы сохранить ordering); нельзя смешивать transactional и non-transactional send в одном продюсере. Caveats: timeout via transaction.timeout.ms (по умолчанию 60 сек), после чего транзакция фенсится (ProducerFencedException). При restart с тем же transactional.id старый PID фенсится, позволяя продолжить. Нюанс: в кластере с downtime контроллера initTransactions() может блокироваться; для EOS в Streams используйте processing.guarantee=exactly_once.
Partitioner: кастомный и дефолтный
Partitioner определяет, в какую партицию идет запись. Дефолтный (DefaultPartitioner): если key=null, round-robin; если key не null, hash(key) % num_partitions (murmur2 hash для consistency).
Кастомный: implement Partitioner interface (partition() method). Полезно для affinity (например, все orders пользователя в одну партицию для ordering). В памяти: вызывается синхронно в send(), overhead — от hash computation.
Нюанс: плохой partitioner может привести к skew (горячие партиции), снижая throughput; всегда учитывайте num_partitions changes.
Retry/backoff политика
Retries (по умолчанию 2147483647) и retry.backoff.ms (100 мс) управляют повторными попытками при transient errors (например, NotLeaderForPartitionException).
Как работает: Sender thread ловит retriable exceptions, backoff (exponential с jitter), затем retry. В idempotence/transactions retry прозрачен, без дубликатов.
В памяти: pending requests в InFlightRequests Map, с timeout per request. Нюанс: высокие retries могут маскировать проблемы (например, network flaps), увеличивая latency; мониторьте retry-rate метрики.
#Java #middle #Kafka #Produser
Idempotent producer обеспечивает exactly-once семантику без дубликатов при ретреях. Включается via enable.idempotence=true, что implicitly устанавливает acks=all, retries>0, max.in.flight.requests.per.connection=5 (или меньше).
Как работает: каждому продюсеру присваивается уникальный producer.id (PID) от брокера при init. Каждому батчу добавляется sequence number (начиная с 0 per partition). Брокер хранит last sequence per PID/partition и отвергает дубликаты (если sequence уже обработан). При ретрее продюсер переотправляет с тем же sequence.
В памяти: продюсер держит Map<TopicPartition, Integer> для sequences. Overhead: минимальный, но требует стабильного соединения (если connection drops, может потребоваться reinitialization). Гарантии: at-least-once becomes exactly-once для одной сессии; не покрывает множественные продюсеры или app restarts (для этого — transactions).
Нюанс: idempotence не влияет на ordering, если max.in.flight=1; но по умолчанию позволяет до 5 in-flight, что может нарушить order (см. senior-нюансы).
Transactions (EOS): transactional.id, initTransactions(), ограничения и caveats
Transactions предоставляют exactly-once semantics (EOS) через топики, включая atomicity: все или ничего для группы send(). Включается via transactional.id (уникальный ID, persistent across restarts).
Процесс: producer.initTransactions() регистрирует PID с брокером (via InitProducerIdRequest), устанавливая epoch. Затем beginTransaction(), send()..., commitTransaction() или abortTransaction(). В commit брокер пишет маркеры (commit/abort) в логи, делая транзакцию видимой для потребителей с isolation.level=read_committed.
В памяти: продюсер держит TransactionManager, который тракает открытые транзакции, pending batches. Batches помечаются transactionally; overhead — дополнительные метаданные в ProduceRequest.
Ограничения: только для idempotent producers; max.in.flight=1 (принудительно, чтобы сохранить ordering); нельзя смешивать transactional и non-transactional send в одном продюсере. Caveats: timeout via transaction.timeout.ms (по умолчанию 60 сек), после чего транзакция фенсится (ProducerFencedException). При restart с тем же transactional.id старый PID фенсится, позволяя продолжить. Нюанс: в кластере с downtime контроллера initTransactions() может блокироваться; для EOS в Streams используйте processing.guarantee=exactly_once.
Partitioner: кастомный и дефолтный
Partitioner определяет, в какую партицию идет запись. Дефолтный (DefaultPartitioner): если key=null, round-robin; если key не null, hash(key) % num_partitions (murmur2 hash для consistency).
Кастомный: implement Partitioner interface (partition() method). Полезно для affinity (например, все orders пользователя в одну партицию для ordering). В памяти: вызывается синхронно в send(), overhead — от hash computation.
Нюанс: плохой partitioner может привести к skew (горячие партиции), снижая throughput; всегда учитывайте num_partitions changes.
Retry/backoff политика
Retries (по умолчанию 2147483647) и retry.backoff.ms (100 мс) управляют повторными попытками при transient errors (например, NotLeaderForPartitionException).
Как работает: Sender thread ловит retriable exceptions, backoff (exponential с jitter), затем retry. В idempotence/transactions retry прозрачен, без дубликатов.
В памяти: pending requests в InFlightRequests Map, с timeout per request. Нюанс: высокие retries могут маскировать проблемы (например, network flaps), увеличивая latency; мониторьте retry-rate метрики.
#Java #middle #Kafka #Produser
Метрики: request-latency, batch-size-avg
Продюсер экспортирует JMX метрики via KafkaProducer.metrics().
Ключевые:
- request-latency-avg: Средняя latency ProduceRequest (от send до response). Включает network + broker processing. Высокая — указывает на bottleneck (медленная сеть, перегруженный брокер).
- batch-size-avg: Средний размер батча в байтах. Идеально близко к batch.size для efficiency; низкий — увеличьте linger.ms.
Другие: record-queue-time-avg (время в accumulator), buffer-bytes (используемая память). Нюанс: используйте MetricsReporter для интеграции с Prometheus; в production мониторьте per-partition метрики для detection skew.
Пример кода
Вот расширенный пример на Java, демонстрирующий idempotence и transactions:
В production добавьте Callback в send() для async handling, и flush() перед close().
Нюансы: max.in.flight.requests.per.connection влияние на ordering, компрессия: zstd vs snappy vs lz4, когда idempotence недостаточно и нужна транзакция
- max.in.flight.requests.per.connection: По умолчанию 5, позволяет параллельные запросы по одному соединению для throughput. Но если >1 и retry происходит, ordering может нарушиться: failed batch retry после successful последующих. В idempotence это разрешено, но для strict ordering установите=1 (снижает throughput на 20-50%). В transactions forcibly=1.
- Компрессия: zstd — лучший ratio (до 5x), но высокий CPU (для высоких данных); snappy — быстрый, низкий CPU, средний ratio (2-3x), идеален для real-time; lz4 — баланс, faster than gzip. В памяти: compression в отдельном thread pool (если compression.type set), overhead — allocate temp buffers. Тестируйте: для text-heavy — zstd; binary — snappy.
- Когда idempotence недостаточно: Idempotence покрывает retries в одной сессии, но не atomicity across topics/partitions или app failures (дубликаты при restart). Транзакции нужны для EOS в multi-topic writes (например, order + inventory update) или с Kafka Streams. Caveat: transactions добавляют 10-20% overhead из-за маркеров и fencing. Используйте если SLA требует no duplicates/loss even on crashes; иначе idempotence достаточно для 99% случаев.
#Java #middle #Kafka #Produser
Продюсер экспортирует JMX метрики via KafkaProducer.metrics().
Ключевые:
- request-latency-avg: Средняя latency ProduceRequest (от send до response). Включает network + broker processing. Высокая — указывает на bottleneck (медленная сеть, перегруженный брокер).
- batch-size-avg: Средний размер батча в байтах. Идеально близко к batch.size для efficiency; низкий — увеличьте linger.ms.
Другие: record-queue-time-avg (время в accumulator), buffer-bytes (используемая память). Нюанс: используйте MetricsReporter для интеграции с Prometheus; в production мониторьте per-partition метрики для detection skew.
Пример кода
Вот расширенный пример на Java, демонстрирующий idempotence и transactions:
import org.apache.kafka.clients.producer.*;
import java.util.Properties;
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("acks", "all");
props.put("enable.idempotence", "true");
props.put("transactional.id", "order-tx-1");
try (KafkaProducer<String, String> producer = new KafkaProducer<>(props)) {
producer.initTransactions();
producer.beginTransaction();
ProducerRecord<String, String> record = new ProducerRecord<>("orders", "key", "value");
producer.send(record);
producer.commitTransaction();
} catch (ProducerFencedException e) {
// Handle fencing, e.g., abort and reinitialize
} catch (KafkaException e) {
// Abort on error
producer.abortTransaction();
}
В production добавьте Callback в send() для async handling, и flush() перед close().
Нюансы: max.in.flight.requests.per.connection влияние на ordering, компрессия: zstd vs snappy vs lz4, когда idempotence недостаточно и нужна транзакция
- max.in.flight.requests.per.connection: По умолчанию 5, позволяет параллельные запросы по одному соединению для throughput. Но если >1 и retry происходит, ordering может нарушиться: failed batch retry после successful последующих. В idempotence это разрешено, но для strict ordering установите=1 (снижает throughput на 20-50%). В transactions forcibly=1.
- Компрессия: zstd — лучший ratio (до 5x), но высокий CPU (для высоких данных); snappy — быстрый, низкий CPU, средний ratio (2-3x), идеален для real-time; lz4 — баланс, faster than gzip. В памяти: compression в отдельном thread pool (если compression.type set), overhead — allocate temp buffers. Тестируйте: для text-heavy — zstd; binary — snappy.
- Когда idempotence недостаточно: Idempotence покрывает retries в одной сессии, но не atomicity across topics/partitions или app failures (дубликаты при restart). Транзакции нужны для EOS в multi-topic writes (например, order + inventory update) или с Kafka Streams. Caveat: transactions добавляют 10-20% overhead из-за маркеров и fencing. Используйте если SLA требует no duplicates/loss even on crashes; иначе idempotence достаточно для 99% случаев.
#Java #middle #Kafka #Produser
👍2
Основы ООП в Java
Глава 1. Классы и объекты
Состояние и поведение: поля и методы
Объектно-ориентированное программирование — это подход, где программа строится вокруг объектов, которые представляют реальные сущности (например, машину, человека или счет в банке).
Java — это чисто объектно-ориентированный язык, где всё (кроме примитивных типов) является объектом. ООП помогает писать масштабируемый код, который легко расширять и поддерживать.
Классы и объекты: Основные понятия
Класс: Это шаблон или "чертеж" для создания объектов. Класс определяет, какие данные (состояние) и действия (поведение) будут у объектов.
Объект: Это экземпляр класса — конкретная реализация шаблона. Например, класс Car может описывать автомобили в общем, а объект — конкретный автомобиль, такой как "красный Ford с номером ABC123".
Пример: Представьте класс Person (Человек). Он может иметь состояние (имя, возраст) и поведение (ходить, говорить). Объекты этого класса — конкретные люди, такие как "Алексей, 35 лет".
Состояние: Поля (Fields)
Состояние объекта определяется полями — переменными внутри класса, которые хранят данные. Поля описывают характеристики объекта.
Объявление полей: Поля объявляются в теле класса, вне методов. Они могут быть примитивными типами (int, double) или ссылочными (String, другие классы).
Модификаторы доступа: Чтобы защитить данные, используйте модификаторы:
private: Доступны только внутри класса (рекомендуется для полей).
public: Доступны везде (избегайте для полей, чтобы сохранить инкапсуляцию).
protected: Доступны в классе и подклассах.
Без модификатора (default): Доступны в пакете.
Пример класса с полями:
Поведение: Методы (Methods)
Поведение объекта определяется методами — функциями внутри класса, которые выполняют действия с данными или изменяют состояние.
Объявление методов: Методы имеют сигнатуру: модификатор, тип возвращаемого значения, имя, параметры в скобках, тело в фигурных скобках.
Типы методов:
Конструкторы: Специальные методы для инициализации объектов. Имя совпадает с именем класса, нет типа возвращаемого значения.
Обычные методы: Выполняют действия, могут возвращать значение или нет (void).
Геттеры и сеттеры: Методы для чтения (get) и записи (set) полей, чтобы обеспечить контролируемый доступ.
Пример с методами:
#Java #для_новичков #beginner #class #object
Глава 1. Классы и объекты
Состояние и поведение: поля и методы
Объектно-ориентированное программирование — это подход, где программа строится вокруг объектов, которые представляют реальные сущности (например, машину, человека или счет в банке).
Java — это чисто объектно-ориентированный язык, где всё (кроме примитивных типов) является объектом. ООП помогает писать масштабируемый код, который легко расширять и поддерживать.
Классы и объекты: Основные понятия
Класс: Это шаблон или "чертеж" для создания объектов. Класс определяет, какие данные (состояние) и действия (поведение) будут у объектов.
Объект: Это экземпляр класса — конкретная реализация шаблона. Например, класс Car может описывать автомобили в общем, а объект — конкретный автомобиль, такой как "красный Ford с номером ABC123".
Пример: Представьте класс Person (Человек). Он может иметь состояние (имя, возраст) и поведение (ходить, говорить). Объекты этого класса — конкретные люди, такие как "Алексей, 35 лет".
Состояние: Поля (Fields)
Состояние объекта определяется полями — переменными внутри класса, которые хранят данные. Поля описывают характеристики объекта.
Объявление полей: Поля объявляются в теле класса, вне методов. Они могут быть примитивными типами (int, double) или ссылочными (String, другие классы).
Модификаторы доступа: Чтобы защитить данные, используйте модификаторы:
private: Доступны только внутри класса (рекомендуется для полей).
public: Доступны везде (избегайте для полей, чтобы сохранить инкапсуляцию).
protected: Доступны в классе и подклассах.
Без модификатора (default): Доступны в пакете.
Пример класса с полями:
public class Person {
// Поля (состояние)
private String name; // Имя человека
private int age; // Возраст человека
}
Здесь name и age — поля, хранящие состояние объекта.
Они private, чтобы внешний код не мог напрямую изменять их (инкапсуляция).
Поведение: Методы (Methods)
Поведение объекта определяется методами — функциями внутри класса, которые выполняют действия с данными или изменяют состояние.
Объявление методов: Методы имеют сигнатуру: модификатор, тип возвращаемого значения, имя, параметры в скобках, тело в фигурных скобках.
Типы методов:
Конструкторы: Специальные методы для инициализации объектов. Имя совпадает с именем класса, нет типа возвращаемого значения.
Обычные методы: Выполняют действия, могут возвращать значение или нет (void).
Геттеры и сеттеры: Методы для чтения (get) и записи (set) полей, чтобы обеспечить контролируемый доступ.
Пример с методами:
public class Person {
private String name;
private int age;
// Конструктор (инициализирует состояние)
public Person(String name, int age) {
this.name = name; // this — ссылка на текущий объект
this.age = age;
}
// Метод (поведение): Вывод информации
public void introduce() {
System.out.println("Привет, меня зовут " + name + ", мне " + age + " лет.");
}
// Геттер для возраста
public int getAge() {
return age;
}
// Сеттер для возраста (с проверкой)
public void setAge(int age) {
if (age > 0) {
this.age = age;
} else {
System.out.println("Возраст не может быть отрицательным!");
}
}
}
Конструктор: Person(String name, int age) устанавливает начальное состояние.
Метод introduce(): Выводит информацию (поведение).
Геттер getAge(): Возвращает значение поля.
Сеттер setAge(int age): Изменяет поле с проверкой (инкапсуляция).
#Java #для_новичков #beginner #class #object
👍4
Создание и использование объектов
Объекты создаются с помощью оператора new.
Пример в методе main:
Как создать это в IntelliJ IDEA
Создайте класс:
В проекте щелкните правой кнопкой на src → New → Java Class.
Назовите Person и добавьте код выше.
Создайте класс Main:
Аналогично создайте Main с методом main.
Запустите:
Правой кнопкой на Main.java → Run 'Main.main()'.
Полезные советы для новичков
Используйте this: Для отличия полей класса от параметров методов (например, this.name = name;).
Конструкторы по умолчанию: Если конструктор не указан, Java создает пустой конструктор public Person() {}.
Множественные конструкторы: Можно иметь несколько конструкторов (перегрузка), перегрузка, но об этом в следующем уроке.
Практика инкапсуляции: Всегда делайте поля private и предоставляйте доступ через геттеры/сеттеры.
Проверки в сеттерах: Добавляйте логику, чтобы избежать некорректных данных.
#Java #для_новичков #beginner #class #object
Объекты создаются с помощью оператора new.
Пример в методе main:
public class Main {
public static void main(String[] args) {
// Создание объекта
Person person1 = new Person("Алексей", 35);
// Вызов метода
person1.introduce(); // Вывод: Привет, меня зовут Алексей, мне 35 лет.
// Использование геттера и сеттера
System.out.println("Текущий возраст: " + person1.getAge());
person1.setAge(36);
System.out.println("Новый возраст: " + person1.getAge());
}
}
new Person("Алексей", 35): Создает объект и вызывает конструктор.
person1.introduce(): Вызывает метод объекта.
Как создать это в IntelliJ IDEA
Создайте класс:
В проекте щелкните правой кнопкой на src → New → Java Class.
Назовите Person и добавьте код выше.
Создайте класс Main:
Аналогично создайте Main с методом main.
Запустите:
Правой кнопкой на Main.java → Run 'Main.main()'.
Полезные советы для новичков
Используйте this: Для отличия полей класса от параметров методов (например, this.name = name;).
Конструкторы по умолчанию: Если конструктор не указан, Java создает пустой конструктор public Person() {}.
Множественные конструкторы: Можно иметь несколько конструкторов (перегрузка), перегрузка, но об этом в следующем уроке.
Практика инкапсуляции: Всегда делайте поля private и предоставляйте доступ через геттеры/сеттеры.
Проверки в сеттерах: Добавляйте логику, чтобы избежать некорректных данных.
#Java #для_новичков #beginner #class #object
👍3