Java Portal | Программирование
13.2K subscribers
1.17K photos
97 videos
37 files
1.09K links
Присоединяйтесь к нашему каналу и погрузитесь в мир для Java-разработчика

Связь: @devmangx

РКН: https://clck.ru/3H4WUg
Download Telegram
Этот проект показывает систему отслеживания местоположения курьера в реальном времени, похожую на то, как это реализовано в Zomato или Swiggy, и построенную на Spring Boot и Apache Kafka.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤣4🌚32💊1
Когда всё живёт в одной базе данных, с транзакциями всё просто.

BEGIN → COMMIT → ROLLBACK.

Но в распределённых системах всё резко усложняется.

В чём реальная проблема ???

В распределённых системах нельзя сделать одну транзакцию на всё сразу по нескольким причинам:

- несколько сервисов
- несколько баз данных
- множество возможных отказов

Классического rollback больше не существует.

И тут появляется паттерн Saga.

Saga это последовательность локальных транзакций, которые координируются между собой.

Каждый шаг:

- выполняет своё изменение
- фиксируется
- и определяет, как себя компенсировать, если дальше что-то пойдёт не так

Глобальной транзакции нет.
Есть eventual consistency.

Простой пример » Создание заказа может включать:

1. Создать заказ

2. Зарезервировать товар

3. Провести оплату

Если на шаге 3 происходит сбой, глобального rollback нет.
Выполняются компенсирующие действия:

- освободить резерв
- пометить заказ как отменённый

Это и есть Saga.

Два способа реализации Saga -

Хореография:

- сервисы реагируют на события
- центрального координатора нет
- слабая связность
- сложнее проследить общий поток

Оркестрация:

- есть компонент-координатор
- поток шагов явно описан
- проще рассуждать о логике
- выше связность

Ни один подход не является универсально лучшим.
Всё зависит от системы и контекста.


Частая ошибка » Считать, что Saga — это замена ACID-транзакциям.

Это не так.

Saga меняет сам контракт:

- допускаются промежуточные состояния
- компенсации проектируются явно
- принимается факт, что система может упасть на середине процесса

Когда Saga действительно имеет смысл

- долгоживущие процессы
- несколько сервисов
- реальные побочные эффекты: платежи, доставки, резервы
- ситуации, где технического rollback не существует

Если всё находится в одной базе данных, Saga не нужна. Здесь легко уйти в оверинжиниринг.

Ключевая мысль:

Saga не убирает сложность.
Она делает её явной.


Явная сложность лучше, чем спрятанная за предположениями, которые больше не работают.

В распределённых системах сбои — это не исключение, а часть нормального потока.

Паттерн Saga не избавляет от всех проблем,
но помогает избежать беспорядка.

А в продакшене это уже большая разница. ☃️

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍51
Java Tip: Начиная с Java 14 можно использовать record для создания компактных неизменяемых объектов, которые просто переносят данные.

» Они короче обычных POJO
» У них из коробки есть equals(), hashCode() и toString()
» По умолчанию они неизменяемые

Классический POJO:

public class Book {
private final String title;
private final int price;

public Book(String title, int price) {
this.title = title;
this.price = price;
}

// геттеры, toString, equals и hashCode
}


Вместо этого можно создать record:

public record Book(String title, int price) {}


Смысл ровно тот же, но без бойлерплейта.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17
Когда ты уже не джун и даже не мидл, на собеседованиях по Java почти не задают вопросы в стиле «что такое HashMap». В ход идут сценарии из реальной жизни.

Разберём типичный кейс.

Твой сервис — оркестратор. Он дергает несколько downstream-сервисов, чтобы обработать запрос.
Один из них проблемный: медленный и периодически отвечает 503 Service Unavailable.

1. Как сделать сервис устойчивым к ненадёжному downstream

Первое, что здесь просится » Circuit Breaker.

Идея простая: если зависимый сервис начинает фейлиться, мы перестаём его дёргать, чтобы:

- не тратить ресурсы,
- не увеличивать латентность,
- не убивать весь сервис каскадными таймаутами.

Реализация с Resilience4j:

Подключаем зависимость и оборачиваем вызов проблемного сервиса в CircuitBreaker.

Ключевые состояния:

Всё нормально. Запросы проходят. Ошибки считаются.

Порог ошибок превышен. Все вызовы сразу фейлятся, downstream даже не вызывается.

Пробный режим. Ограниченное число запросов, чтобы проверить — ожил сервис или нет.

Пример:

@CircuitBreaker(name = "inventoryService", fallbackMethod = "inventoryFallback")
public InventoryResponse getInventory(String productId) {
return inventoryClient.getInventory(productId);
}


Конфиг обычно задаётся через application.yml:

- процент ошибок
- sliding window
- waitDurationInOpenState
- permittedNumberOfCallsInHalfOpenState

2. Что делать, когда circuit открыт (fallback)

Тут нет универсального ответа » зависит от бизнеса.

Типовые варианты:

🔹Кэш

Если данные не критичны к свежести:

- Redis
- локальный cache
- stale-данные лучше, чем 503

private InventoryResponse inventoryFallback(String productId, Throwable ex) {
return cacheService.getInventory(productId);
}


🔹Дефолтный ответ

Если можно вернуть «безопасное» значение:

- пустой список
- available = false
- статус UNKNOWN

🔹Очередь

Если запрос важен, но не срочный:

- кладём событие в Kafka / Rabbit
- обрабатываем асинхронно
- отвечаем клиенту 202 Accepted

На практике часто комбинируют:
кэш + деградация функциональности.

3. Глобальная обработка ошибок через @RestControllerAdvice

Чтобы не размазывать try/catch по всему коду, делаем централизованный обработчик.

Пример бизнес-исключения

public class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException(String id) {
super("Product not found: " + id);
}
}


Глобальный handler

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProductNotFound(ProductNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"PRODUCT_NOT_FOUND",
ex.getMessage()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}

JSON-ответ
{
"code": "PRODUCT_NOT_FOUND",
"message": "Product not found: 123"
}


Плюсы подхода:

- чистые контроллеры
- единый формат ошибок
- нормальная мапа бизнес-ошибок на HTTP-статусы

Итог

На таком вопросе проверяют не знание аннотаций, а мышление:

- понимаешь ли ты отказоустойчивость
- умеешь ли деградировать сервис
- разделяешь ли бизнес-ошибки и технические фейлы

Это уже разговор не про «Java», а про архитектуру продакшн-сервисов

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍103
Spring Boot наконец получил нативную поддержку gRPC

Забудьте о сторонних стартерах и костылях — Spring gRPC 1.0 GA уже здесь. Теперь можно строить высокопроизводительные RPC-сервисы с Protocol Buffers прямо из коробки, без плясок с бубном.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
9
Kubernetes-совет:

Java-приложения обычно требуют больше CPU на старте, чем во время обычной работы ☕️

Эта политика Kyverno:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: resize-pod-policy
spec:
mutateExistingOnPolicyUpdate: false
rules:
- name: resize-pod-policy
match:
any:
- resources:
kinds:
- Pod/status
- Pod
preconditions:
all:
- key: "{{request.object.status.containerStatuses[0].ready}}"
operator: Equals
value: true
mutate:
targets:
- apiVersion: v1
kind: Pod.resize
name: "{{request.object.iss.onetadata.name}}"
patchStrategicMerge:
spec:
containers:
- (name): sample-app-on-kubernetes
resources:
limits:
cpu: 0.5


- отслеживает момент завершения старта pod’а
- обновляет ресурсы pod’а прямо на месте
- снижает CPU-лимиты после старта

Итог: более быстрый запуск и меньшие затраты

Вот моя статья об этом

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔61👍1
Java-совет: избегайте глубоко вложенных if/else.

Используйте guard clauses (ранние выходы из метода) вместо этого.

 Вложенные if-else:

public void processOrder(Order order) {
if (order != null) {
if (order.isPaid()) {
if (order.getItems().size() > 0) {
// Обработка заказа
System.out.println("Заказ обработан");
} else {
System.out.println("В заказе нет позиций");
}
} else {
System.out.println("Заказ не оплачен");
}
} else {
System.out.println("Заказ равен null");
}
}

Использование guard clauses (читается проще):

public void processOrder(Order order) {
if (order == null) {
System.out.println("Заказ равен null");
return;
}

if (!order.isPaid()) {
System.out.println("Заказ не оплачен");
return;
}

if (order.getItems().isEmpty()) {
System.out.println("В заказе нет позиций");
return;
}

// Обработка заказа
System.out.println("Заказ обработан");


👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
👍94
🔴 Завтра тестовое собеседование с Java-разработчиком

24 декабря(уже завтра!) в 19:00 по мск приходи онлайн на открытое собеседование, чтобы посмотреть на настоящее интервью на Middle Java-разработчика.

Как это будет:
📂 Сергей Чамкин, старший разработчик из Uzum, ex-WildBerries, будет задавать реальные вопросы и задачи разработчику-добровольцу
📂 Cергей будет комментировать каждый ответ респондента, чтобы дать понять чего от вас ожидает собеседующий на интервью
📂 В конце можно будет задать любой вопрос Сергею

Это бесплатно. Эфир проходит в рамках менторской программы от ШОРТКАТ для Java-разработчиков, которые хотят повысить свой грейд, ЗП и прокачать скиллы.

Переходи в нашего бота, чтобы получить ссылку на эфир →
@shortcut_sh_bot

Реклама.
О рекламодателе.
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Создаём первый Minecraft мод и подробно разбираем Mixin. Просто и понятно

Эта статья была написана из-за отсутствия адекватных русскоязычных руководств по моддингу, особенно для новых версий Minecraft. То, что описано в статье, будет применимо для таких загрузчиков модов, как Fabric и Forge, а также для реализации различных функций.

👉 Java Portal
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍1