С какими сложными или интересными техническими задачами вам приходилось работать?

Ответ

1. Высоконагруженные и параллельные системы:

  • Оптимизация производительности с использованием ConcurrentHashMap, BlockingQueue и примитивов синхронизации из java.util.concurrent.

2. Реактивное программирование:

  • Разработка неблокирующих REST API с использованием Spring WebFlux и Project Reactor (Mono, Flux). Это позволяет эффективно использовать ресурсы при большом количестве одновременных соединений.
    @RestController
    public class ReactiveUserController {
       @GetMapping("/users/{id}")
       public Mono<User> getUser(@PathVariable String id) {
           // Репозиторий возвращает Mono/Flux (например, ReactiveMongoRepository)
           return userRepository.findById(id);
       }
    }

3. Кастомная обработка через AOP (Aspect-Oriented Programming):

  • Создание аннотаций и аспектов Spring AOP для сквозной логики: логирование вызовов методов, замер времени выполнения, безопасность.

4. Событийно-ориентированная архитектура (EDA):

  • Интеграция Apache Kafka для реализации паттернов Event Sourcing и CQRS, обеспечение слабой связности и масштабируемости сервисов.

5. Оптимизация работы с БД:

  • Использование @EntityGraph в Spring Data JPA для решения проблемы N+1 запроса.
  • Настройка batch-обработки (jdbc.batch_size) для массовых вставок и обновлений.

Ответ 18+ 🔞

А, слушай, тут у меня один знакомый, который в джаве как рыба в воде, рассказывал, как он системы пилит, чтобы они не ложились под нагрузкой, как пьяный студент после сессии. Ну, я запомнил, сейчас тебе перескажу, только без его матерных отступлений, а то он там через слово про «ёперный театр» вспоминал.

1. Про то, как не подраться потокам в одной куче. Вот представь: у тебя двадцать потоков лезут в одну HashMap что-то записать. Это ж гарантированный пиздец, они друг другу мозги вынесут. Поэтому умные дядьки придумали java.util.concurrent. Там ConcurrentHashMap — она как хитрая жопа, умеет сама разбираться, кто куда лезет, без глобальных драк. А BlockingQueue — это вообще песня: один поток кладёт задачу, другой спокойно забирает, и никто никого не ждёт вхолостую, как идиот. В общем, не надо изобретать свои костыли с synchronized на каждом углу, всё уже придумано.

2. Реактивное программирование, или «Не тормози — сникерсни». Это когда твой сервис должен общаться с тысячей клиентов одновременно, а старый добрый Spring MVC на каждый запрос выделяет отдельный поток и в итоге захлёбывается, как мудак в луже. Тут на помощь приходит Spring WebFlux. Он неблокирующий, работает на событийных циклах. Вместо того чтобы тупо ждать ответа от базы, он говорит: «Ладно, братан, как получишь данные — позови». И пока один запрос ждёт, он других обрабатывает. Выглядит это примерно так:

@RestController
public class ReactiveUserController {
    @GetMapping("/users/{id}")
    public Mono<User> getUser(@PathVariable String id) {
        // Репозиторий возвращает Mono/Flux (например, ReactiveMongoRepository)
        return userRepository.findById(id);
    }
}

Видишь Mono? Это как обещание, что когда-нибудь тут будет пользователь. А Flux — это поток таких обещаний. Главное, не пытайся вызвать на этом .get() или что-то такое — охуеешь от ошибок. Нужно подписываться и реагировать.

3. AOP — волшебная палочка для ленивых. Ну вот есть у тебя куча методов в сервисе, и везде нужно, например, логировать вход и выход, или замерять время. Писать это в каждом методе — это же пиздец какой-то, однообразная работа, как долбить стену ложкой. Надо заюзать Spring AOP. Создаёшь аннотацию, типа @LogExecutionTime, пишешь один раз аспект, который будет перехватывать все методы с этой аннотацией, и вуаля — вся магия работает сама. Чистая декларативность, блядь. Подозрение ебать чувствую, что это чёрная магия, но она работает.

4. Кафка, или «Давайте жить дружно, но на расстоянии». Когда у тебя микросервисов, как собак нерезаных, и они начинают друг другу синхронно звонить по HTTP, получается такая каша, что волосы дыбом. Тут помогает событийно-ориентированная архитектура. Ставишь Apache Kafka — этакую общую очередь для сообщений. Один сервис крикнул в неё: «Эй, заказ создан!» — и похуй, пошёл дальше работать. Другие сервисы, которые подписаны на эти события, сами в удобном для себя темпе их подхватят и обработают. Слабая связность, масштабируемость — красота. Паттерны Event Sourcing и CQRS на этом и строятся. Правда, надо быть готовым к тому, что теперь «доставлено» не значит «обработано», но это уже другая история.

5. База данных — не враг, но её надо ублажать. Самый частый косяк в Spring Data JPA — проблема N+1. Запросил список пользователей, а он для каждого пользователя лезет в базу за его заказами отдельным запросом. В итоге вместо одного запроса — овердохуища. Лечится это аннотацией @EntityGraph, которая говорит Хибернейту: «Эй, дружок, тащи сразу всё связанное, за один проход, не позорься». А если надо вставить десять тысяч записей, а не одну? Делать это по одной — это же ебать мои старые костыли, час ждать. Надо включить batch-обработку, прописав в настройках jdbc.batch_size. Тогда Хибернейт будет пачками отправлять инсерты, и всё пролетит в разы быстрее.

Вот, примерно так. Главное — не бояться этих инструментов, а то некоторые джуны смотрят на Mono и Flux как кот сука собака на новый корм, и лепят везде блокирующий код. А потом удивляются, почему всё падает.