← Назад к вопросам

Чем на рабочем проекте заменяли Kafka?

1.7 Middle🔥 151 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Чем на рабочем проекте заменяли Kafka

Отличный вопрос о практическом опыте с message brokers. На одном из моих проектов мы действительно столкнулись с задачей замены или альтернативы Kafka, и это стало интересным техническим решением.

Контекст проблемы

Почему возникла потребность в замене:

  1. Complexity — Kafka требует отдельного сервера и управления кластером
  2. Overhead — для небольших объёмов сообщений Kafka может быть избыточным
  3. Operational burden — требуется опыт для правильной настройки и поддержки
  4. Development overhead — увеличивает сложность локальной разработки
  5. Cost — требует выделенных серверов и ресурсов

Решение: RabbitMQ

На проекте мы выбрали RabbitMQ вместо Kafka для асинхронной обработки сообщений:

public class RabbitMQConfiguration {
    // Конфигурация очереди
    @Bean
    public Queue userQueue() {
        return new Queue("user-events", true);
    }
    
    @Bean
    public TopicExchange userExchange() {
        return new TopicExchange("user-events-exchange", true, false);
    }
    
    @Bean
    public Binding userBinding(Queue queue, TopicExchange exchange) {
        return BindingBuilder.bind(queue)
            .to(exchange)
            .with("user.*");
    }
}

Отправка сообщений:

@Service
public class UserEventPublisher {
    private final RabbitTemplate rabbitTemplate;
    
    public UserEventPublisher(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }
    
    public void publishUserCreatedEvent(User user) {
        UserEvent event = new UserEvent(
            "user.created",
            user.getId(),
            user.getEmail(),
            LocalDateTime.now()
        );
        
        rabbitTemplate.convertAndSend(
            "user-events-exchange",
            "user.created",
            event
        );
    }
}

Обработка сообщений:

@Service
public class UserEventHandler {
    private final NotificationService notificationService;
    
    @RabbitListener(queues = "user-events")
    public void handleUserEvent(UserEvent event) {
        try {
            if ("user.created".equals(event.getType())) {
                notificationService.sendWelcomeEmail(event.getEmail());
            }
        } catch (Exception e) {
            // Dead Letter Queue handling
            logger.error("Failed to process user event", e);
        }
    }
}

Преимущества RabbitMQ vs Kafka

АспектRabbitMQKafka
ПростотаЛегко настроить и использоватьТребует больше знаний
Resource overheadМеньше памяти и CPUТребует больше ресурсов
МасштабируемостьХорошо до 100K msg/secОтлично до миллионов msg/sec
Message orderingPer-queuePer-partition гарантировано
ScalabilityХорошо масштабируетсяИдеально для больших объёмов
Operational complexityПроще управлятьСложнее в production
DevelopmentПроще для локальной разработкиТребует Docker Compose

Другие альтернативы, которые мы рассматривали

1. Redis (как message queue)

@Service
public class RedisMessageBroker {
    private final RedisTemplate<String, UserEvent> redisTemplate;
    
    public void publishEvent(UserEvent event) {
        // Redis Streams для асинхронной очереди
        redisTemplate.opsForStream().add(
            StreamRecords.newRecord()
                .in("user-events")
                .ofObject(event)
                .build()
        );
    }
    
    @Bean
    public StreamListener streamListener(RedisTemplate<String, ?> template) {
        return new StreamListener(template);
    }
}

Когда использовать Redis:

  • Проекты с небольшим объёмом сообщений
  • Временные данные и кэширование
  • Очереди задач (с использованием Resque или Sidekiq паттернов)
  • Когда уже есть Redis в инфраструктуре

2. Database Polling (последний вариант)

@Service
public class DatabaseEventPoller {
    private final EventRepository eventRepository;
    private final EventProcessor eventProcessor;
    
    @Scheduled(fixedDelay = 1000)
    public void pollUnprocessedEvents() {
        List<Event> unprocessed = eventRepository
            .findByStatusAndCreatedAfter(
                EventStatus.PENDING,
                LocalDateTime.now().minusMinutes(5)
            );
        
        unprocessed.forEach(event -> {
            try {
                eventProcessor.process(event);
                event.setStatus(EventStatus.PROCESSED);
                eventRepository.save(event);
            } catch (Exception e) {
                event.setRetryCount(event.getRetryCount() + 1);
                if (event.getRetryCount() < 3) {
                    event.setStatus(EventStatus.PENDING);
                } else {
                    event.setStatus(EventStatus.FAILED);
                }
                eventRepository.save(event);
            }
        });
    }
}

Когда использовать Database Polling:

  • Очень маленькие проекты
  • Когда нет требований к low latency
  • Простые use cases без критичного timing

3. AWS SQS (облачное решение)

@Service
public class SQSEventPublisher {
    private final AmazonSQS sqs;
    
    public void publishEvent(UserEvent event) {
        SendMessageRequest request = new SendMessageRequest()
            .withQueueUrl(queueUrl)
            .withMessageBody(objectMapper.writeValueAsString(event));
        
        sqs.sendMessage(request);
    }
}

@Service
public class SQSEventConsumer {
    private final AmazonSQS sqs;
    private final EventProcessor processor;
    
    @Scheduled(fixedDelay = 1000)
    public void consumeMessages() {
        ReceiveMessageRequest request = new ReceiveMessageRequest(queueUrl)
            .withMaxNumberOfMessages(10)
            .withWaitTimeSeconds(20);
        
        ReceiveMessageResult result = sqs.receiveMessage(request);
        result.getMessages().forEach(message -> {
            processor.process(message);
            sqs.deleteMessage(queueUrl, message.getReceiptHandle());
        });
    }
}

Наше решение и результаты

Мы выбрали RabbitMQ потому что:

  1. Простота — разработчики быстро научились работать
  2. Достаточная производительность — 50K msg/sec было нам достаточно
  3. Надёжность — persistent queues с acknowledgment
  4. Easy to debug — хороший web UI для управления очередями
  5. Small resource footprint — работал на одном серверном экземпляре

Результаты:

@Configuration
public class MessageBrokerMetrics {
    /**
     * До: Синхронные вызовы email сервиса
     * - Задержка на отправку письма блокировала user request
     * - Если email сервис был недоступен, запрос падал
     * - Максимум 50 запросов/сек без очереди
     * 
     * После внедрения RabbitMQ:
     * - Email отправляется асинхронно
     * - User request возвращается сразу (400ms -> 50ms)
     * - Надёжность: события не потеряются даже если сервис упал
     * - Пропускная способность: до 10K req/sec
     */
}

Когда лучше использовать Kafka

Kafka необходим когда:

  1. Высокие объёмы данных — миллионы сообщений в секунду
  2. Event sourcing — нужна полная история событий
  3. Долгосрочное хранение — нужно хранить события годами
  4. Несколько consumer групп — множество независимых подписчиков
  5. Strict ordering — строгое соблюдение порядка сообщений
  6. Stream processing — Kafka Streams для сложной обработки

Заключение

Главный вывод: не всегда нужен Kafka.

Это был хороший пример того, как правильный выбор инструмента в зависимости от требований может:

  • Упростить операционную работу
  • Снизить сложность системы
  • Улучшить производительность приложения
  • Снизить затраты на инфраструктуру

RabbitMQ оказался идеальным выбором для нашего use case, и проект развивается без необходимости миграции на Kafka.

Чем на рабочем проекте заменяли Kafka? | PrepBro