yudaocode/ruoyi-vue-pro:Kafka集成实战

yudaocode/ruoyi-vue-pro:Kafka集成实战

【免费下载链接】ruoyi-vue-pro 🔥 官方推荐 🔥 RuoYi-Vue 全新 Pro 版本,优化重构所有功能。基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 微信小程序,支持 RBAC 动态权限、数据权限、SaaS 多租户、Flowable 工作流、三方登录、支付、短信、商城、CRM、ERP、AI 等功能。你的 ⭐️ Star ⭐️,是作者生发的动力! 【免费下载链接】ruoyi-vue-pro 项目地址: https://gitcode.com/yudaocode/ruoyi-vue-pro

引言:为什么选择Kafka作为消息队列?

在现代分布式系统中,消息队列(Message Queue)已成为解耦系统组件、提升系统可靠性和扩展性的关键技术。Apache Kafka作为分布式流处理平台,凭借其高吞吐量、低延迟、持久化存储和水平扩展能力,成为企业级应用的首选消息队列解决方案。

在ruoyi-vue-pro项目中,Kafka被深度集成到多个核心模块中,为系统提供了强大的异步消息处理能力。本文将深入探讨如何在ruoyi-vue-pro项目中实战使用Kafka,涵盖从基础配置到高级特性的完整实现。

一、Kafka在ruoyi-vue-pro中的架构设计

1.1 整体架构概览

mermaid

1.2 核心组件职责

组件职责描述应用场景
KafkaTemplate消息发送模板统一消息发送接口
@KafkaListener消息消费注解声明式消息监听
TenantKafkaProducerInterceptor多租户拦截器租户上下文传递
KafkaWebSocketMessageSenderWebSocket消息发送实时消息广播

二、环境准备与依赖配置

2.1 Maven依赖配置

在ruoyi-vue-pro项目中,Kafka依赖通过Spring Boot Starter自动管理:

<!-- pom.xml 中的依赖管理 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>${spring-kafka.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

2.2 配置文件示例

# application.yml 配置
spring:
  kafka:
    bootstrap-servers: localhost:9092
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      acks: all
      retries: 3
    consumer:
      group-id: ruoyi-vue-pro-group
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      auto-offset-reset: earliest
      properties:
        spring.json.trusted.packages: "cn.iocoder.yudao.framework.websocket.core.sender.kafka"

三、核心功能实现详解

3.1 消息生产者实现

3.1.1 基础消息发送
@Service
@RequiredArgsConstructor
public class KafkaMessageService {
    
    private final KafkaTemplate<Object, Object> kafkaTemplate;
    
    /**
     * 发送简单消息
     */
    public void sendSimpleMessage(String topic, String message) {
        kafkaTemplate.send(topic, message);
    }
    
    /**
     * 发送带键的消息
     */
    public void sendMessageWithKey(String topic, String key, String message) {
        kafkaTemplate.send(topic, key, message);
    }
    
    /**
     * 发送对象消息
     */
    public void sendObjectMessage(String topic, Object payload) {
        kafkaTemplate.send(topic, payload);
    }
}
3.1.2 多租户消息发送拦截器

ruoyi-vue-pro实现了多租户场景下的Kafka消息拦截器:

public class TenantKafkaProducerInterceptor implements ProducerInterceptor<Object, Object> {

    @Override
    public ProducerRecord<Object, Object> onSend(ProducerRecord<Object, Object> record) {
        Long tenantId = TenantContextHolder.getTenantId();
        if (tenantId != null) {
            Headers headers = (Headers) ReflectUtil.getFieldValue(record, "headers");
            headers.add(HEADER_TENANT_ID, tenantId.toString().getBytes());
        }
        return record;
    }

    // 其他方法实现...
}

3.2 消息消费者实现

3.2.1 基础消息消费
@Component
@Slf4j
public class KafkaMessageConsumer {
    
    /**
     * 监听指定topic的消息
     */
    @KafkaListener(
        topics = "${spring.kafka.topic.user-notification}",
        groupId = "${spring.kafka.consumer.group-id}"
    )
    public void consumeUserNotification(String message) {
        log.info("收到用户通知消息: {}", message);
        // 业务处理逻辑
    }
    
    /**
     * 监听多个topic
     */
    @KafkaListener(
        topics = {"topic1", "topic2", "topic3"},
        groupId = "${spring.kafka.consumer.group-id}"
    )
    public void consumeMultipleTopics(String message) {
        log.info("收到多topic消息: {}", message);
    }
}
3.2.2 对象消息消费
@Component
@Slf4j
public class ObjectMessageConsumer {
    
    @KafkaListener(
        topics = "${spring.kafka.topic.websocket}",
        groupId = "${spring.kafka.consumer.group-id}"
    )
    public void consumeWebSocketMessage(KafkaWebSocketMessage message) {
        log.info("收到WebSocket广播消息: {}", message);
        // 处理WebSocket消息逻辑
    }
}

3.3 WebSocket与Kafka集成实战

ruoyi-vue-pro通过Kafka实现了分布式WebSocket消息广播:

@Slf4j
public class KafkaWebSocketMessageSender extends AbstractWebSocketMessageSender {

    private final KafkaTemplate<Object, Object> kafkaTemplate;
    private final String topic;

    public KafkaWebSocketMessageSender(WebSocketSessionManager sessionManager,
                                       KafkaTemplate<Object, Object> kafkaTemplate,
                                       String topic) {
        super(sessionManager);
        this.kafkaTemplate = kafkaTemplate;
        this.topic = topic;
    }

    /**
     * 通过Kafka广播消息
     */
    private void sendKafkaMessage(String sessionId, Long userId, Integer userType,
                                  String messageType, String messageContent) {
        KafkaWebSocketMessage mqMessage = new KafkaWebSocketMessage()
                .setSessionId(sessionId).setUserId(userId).setUserType(userType)
                .setMessageType(messageType).setMessageContent(messageContent);
        try {
            kafkaTemplate.send(topic, mqMessage).get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("[sendKafkaMessage][发送消息({}) 到Kafka失败]", mqMessage, e);
        }
    }
}

四、高级特性与最佳实践

4.1 消息确认机制

// 发送消息并获取发送结果
public void sendMessageWithCallback(String topic, String message) {
    ListenableFuture<SendResult<Object, Object>> future = kafkaTemplate.send(topic, message);
    
    future.addCallback(new ListenableFutureCallback<SendResult<Object, Object>>() {
        @Override
        public void onSuccess(SendResult<Object, Object> result) {
            log.info("消息发送成功: {}", result.getRecordMetadata());
        }
        
        @Override
        public void onFailure(Throwable ex) {
            log.error("消息发送失败: {}", ex.getMessage());
        }
    });
}

4.2 事务消息处理

@Transactional
public void processWithTransaction(String topic, String message) {
    // 数据库操作
    userRepository.save(new User());
    
    // Kafka消息发送(在同一事务中)
    kafkaTemplate.send(topic, message);
    
    // 其他业务操作
}

4.3 消息重试与死信队列

@Configuration
public class KafkaRetryConfig {
    
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(
            ConsumerFactory<String, String> consumerFactory) {
        
        ConcurrentKafkaListenerContainerFactory<String, String> factory = 
            new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory);
        
        // 配置重试机制
        factory.setRetryTemplate(retryTemplate());
        
        // 配置死信队列
        factory.setRecoveryCallback(context -> {
            ConsumerRecord<?, ?> record = (ConsumerRecord<?, ?>) context.getAttribute("record");
            log.error("消息处理失败,发送到死信队列: {}", record);
            // 发送到死信队列逻辑
            return null;
        });
        
        return factory;
    }
    
    private RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3));
        retryTemplate.setBackOffPolicy(new FixedBackOffPolicy());
        return retryTemplate;
    }
}

五、性能优化与监控

5.1 生产者性能优化

spring:
  kafka:
    producer:
      batch-size: 16384  # 批量大小
      buffer-memory: 33554432  # 缓冲区内存
      linger-ms: 5  # 发送延迟
      compression-type: snappy  # 压缩类型

5.2 消费者性能优化

spring:
  kafka:
    consumer:
      max-poll-records: 500  # 每次拉取最大记录数
      fetch-max-wait-ms: 500  # 拉取最大等待时间
      fetch-min-bytes: 1  # 拉取最小字节数

5.3 监控指标配置

@Configuration
public class KafkaMetricsConfig {
    
    @Bean
    public MeterRegistryCustomizer<MeterRegistry> kafkaMetrics() {
        return registry -> {
            registry.config().commonTags("application", "ruoyi-vue-pro");
        };
    }
}

六、常见问题与解决方案

6.1 消息顺序性问题

// 确保相同key的消息发送到同一分区
public void sendOrderedMessage(String topic, String key, String message) {
    kafkaTemplate.send(topic, key, message);
}

6.2 消息重复消费问题

@Component
@Slf4j
public class IdempotentConsumer {
    
    private final Set<String> processedMessageIds = ConcurrentHashMap.newKeySet();
    
    @KafkaListener(topics = "order-topic")
    public void consumeOrderMessage(OrderMessage message) {
        if (processedMessageIds.contains(message.getId())) {
            log.info("消息已处理,跳过: {}", message.getId());
            return;
        }
        
        // 处理消息
        processOrder(message);
        
        // 记录已处理消息
        processedMessageIds.add(message.getId());
    }
}

6.3 消费者偏移量管理

@KafkaListener(topics = "user-topic", groupId = "user-group")
public void consumeUserMessage(
        ConsumerRecord<String, String> record,
        Acknowledgment acknowledgment) {
    
    try {
        // 处理消息
        processUserMessage(record.value());
        
        // 手动提交偏移量
        acknowledgment.acknowledge();
    } catch (Exception e) {
        log.error("消息处理失败: {}", record.value(), e);
        // 不提交偏移量,等待重试
    }
}

七、实战案例:订单状态变更通知

7.1 消息生产者实现

@Service
@RequiredArgsConstructor
public class OrderStatusProducer {
    
    private final KafkaTemplate<String, OrderStatusMessage> kafkaTemplate;
    
    public void sendOrderStatusChange(Long orderId, String oldStatus, String newStatus) {
        OrderStatusMessage message = new OrderStatusMessage()
                .setOrderId(orderId)
                .setOldStatus(oldStatus)
                .setNewStatus(newStatus)
                .setTimestamp(System.currentTimeMillis());
        
        kafkaTemplate.send("order-status-topic", orderId.toString(), message);
    }
}

7.2 消息消费者实现

@Component
@Slf4j
public class OrderStatusConsumer {
    
    @KafkaListener(
        topics = "order-status-topic",
        groupId = "order-status-group"
    )
    public void consumeOrderStatusChange(OrderStatusMessage message) {
        log.info("订单状态变更: 订单ID={}, 从{}变为{}", 
                message.getOrderId(), message.getOldStatus(), message.getNewStatus());
        
        // 发送通知给相关用户
        notifyUsers(message);
        
        // 更新相关系统状态
        updateSystemStatus(message);
    }
}

总结

通过本文的详细讲解,我们深入了解了ruoyi-vue-pro项目中Kafka的集成实战。从基础配置到高级特性,从消息生产到消费,从性能优化到监控告警,Kafka为ruoyi-vue-pro提供了强大的异步消息处理能力。

关键要点总结:

  1. 架构设计:采用生产者-消费者模式,支持多租户和WebSocket集成
  2. 核心组件:KafkaTemplate、@KafkaListener、多租户拦截器
  3. 高级特性:事务消息、重试机制、死信队列
  4. 性能优化:批量处理、压缩、监控指标
  5. 实战案例:订单状态变更通知等业务场景

Kafka在ruoyi-vue-pro中的成功集成,为构建高可用、可扩展的分布式系统提供了坚实的技术基础。通过合理的配置和最佳实践,可以充分发挥Kafka的性能优势,满足各种复杂的业务场景需求。

【免费下载链接】ruoyi-vue-pro 🔥 官方推荐 🔥 RuoYi-Vue 全新 Pro 版本,优化重构所有功能。基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 微信小程序,支持 RBAC 动态权限、数据权限、SaaS 多租户、Flowable 工作流、三方登录、支付、短信、商城、CRM、ERP、AI 等功能。你的 ⭐️ Star ⭐️,是作者生发的动力! 【免费下载链接】ruoyi-vue-pro 项目地址: https://gitcode.com/yudaocode/ruoyi-vue-pro

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值