Kafka 和 RabbitMQ 使用:消息队列的强大工具

Kafka 和 RabbitMQ 入门:消息队列的强大工具

今天我们来深入探讨两种流行的消息队列工具:Apache Kafka 和 RabbitMQ。这两者在分布式系统、微服务架构和实时数据处理中扮演重要角色,广泛用于解耦系统、异步处理和事件驱动架构。本文将带你从零搭建一个简单的消息队列应用,分别使用 Kafka 和 RabbitMQ,结合 Spring Boot 实现消息生产和消费,适合初学者快速上手,同时为有经验的开发者提供进阶建议和优化思路。

Kafka 以高吞吐量和分布式日志处理见长,适合大数据场景;RabbitMQ 以灵活的路由和易用性著称,适合传统企业应用。本文基于 Kafka 3.8.x 和 RabbitMQ 3.13.x,使用 Maven 和 Spring Boot 构建一个简单的用户消息处理示例。让我们开始吧!

前置准备

在开始之前,确保开发环境已就绪:

  • JDK:推荐 JDK 17(Spring Boot 3.x 要求 JDK 17+,2.x 支持 JDK 8+)。

  • Maven:用于依赖管理,确保配置好环境变量。

  • IDE:IntelliJ IDEA 或 Eclipse,推荐使用 Spring Initializr 插件生成项目。

  • 消息队列

    • Kafka:安装 Kafka 3.8.x(需要 ZooKeeper 或 KRaft 模式,推荐 Docker)。
    • RabbitMQ:安装 RabbitMQ 3.13.x(推荐 Docker)。
  • 项目结构 :为 Kafka 和 RabbitMQ 各创建一个 Spring Boot 项目,目录如下:

    复制代码
    src
    ├── main
    │   ├── java
    │   │   └── com.example.demo
    │   │       ├── config
    │   │       ├── controller
    │   │       └── model
    │   ├── resources
    │   │   └── application.properties  // 配置文件
    └── test
        └── java

安装消息队列

  • Kafkadocker run -d -p 9092:9092 apache/kafka:3.8.0
  • RabbitMQdocker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management
  • 使用 Spring Initializr 生成两个项目:
    • Kafka 项目:添加 Spring for Apache Kafka 依赖。
    • RabbitMQ 项目:添加 Spring for RabbitMQ 依赖。

步骤 1: 引入 Maven 依赖

为 Kafka 和 RabbitMQ 项目分别配置 pom.xml

Kafka 项目

xml 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>kafka-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/>
    </parent>

    <dependencies>
        <!-- Spring Boot Web 启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Kafka 启动器 -->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

RabbitMQ 项目

xml 复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>rabbitmq-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/>
    </parent>

    <dependencies>
        <!-- Spring Boot Web 启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring RabbitMQ 启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

说明

  • spring-kafka:提供 Kafka 生产者和消费者支持。
  • spring-boot-starter-amqp:支持 RabbitMQ 的 AMQP 协议。
  • 版本提示:Spring Boot 3.x 需 JDK 17+,若用 JDK 8,选择 2.x。

步骤 2: 配置消息队列

Kafka 配置

kafka-demo/src/main/resources/application.properties 中配置:

properties 复制代码
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=my-group
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.trusted.packages=com.example.demo.model

要点

  • bootstrap-servers:Kafka 服务器地址。
  • JsonSerializer/Deserializer:支持 JSON 序列化。
  • trusted.packages:确保反序列化安全。

RabbitMQ 配置

rabbitmq-demo/src/main/resources/application.properties 中配置:

properties 复制代码
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.queue=user-queue
spring.rabbitmq.exchange=user-exchange
spring.rabbitmq.routingkey=user.routingkey

要点

  • 默认用户/密码为 guest,生产环境需修改。
  • 定义队列、交换机和路由键。

步骤 3: 创建模型

定义 UserMessage 模型(com.example.demo.model.UserMessage),用于两个项目,需实现 Serializable

java 复制代码
package com.example.demo.model;

import java.io.Serializable;

public class UserMessage implements Serializable {
    private Long id;
    private String name;

    // Constructors
    public UserMessage() {}
    public UserMessage(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

步骤 4: 配置和实现消息生产与消费

Kafka 项目

配置

创建 KafkaConfigcom.example.demo.config.KafkaConfig):

java 复制代码
package com.example.demo.config;

import com.example.demo.model.UserMessage;
import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.TopicBuilder;

@Configuration
public class KafkaConfig {
    @Bean
    public NewTopic userTopic() {
        return TopicBuilder.name("user-topic").partitions(1).replicas(1).build();
    }
}
生产者

创建 UserControllercom.example.demo.controller.UserController):

java 复制代码
package com.example.demo.controller;

import com.example.demo.model.UserMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/kafka")
public class UserController {

    @Autowired
    private KafkaTemplate<String, UserMessage> kafkaTemplate;

    @PostMapping("/send")
    public String sendMessage(@RequestBody UserMessage message) {
        kafkaTemplate.send("user-topic", String.valueOf(message.getId()), message);
        return "Message sent to Kafka";
    }
}
消费者

创建 UserConsumercom.example.demo.config.UserConsumer):

java 复制代码
package com.example.demo.config;

import com.example.demo.model.UserMessage;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

@Component
public class UserConsumer {

    @KafkaListener(topics = "user-topic", groupId = "my-group")
    public void listen(UserMessage message) {
        System.out.println("Received: ID=" + message.getId() + ", Name=" + message.getName());
    }
}

RabbitMQ 项目

配置

创建 RabbitConfigcom.example.demo.config.RabbitConfig):

java 复制代码
package com.example.demo.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {

    @Bean
    public Queue queue() {
        return new Queue("user-queue", true);
    }

    @Bean
    public TopicExchange exchange() {
        return new TopicExchange("user-exchange");
    }

    @Bean
    public Binding binding(Queue queue, TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("user.routingkey");
    }
}
生产者

创建 UserControllercom.example.demo.controller.UserController):

java 复制代码
package com.example.demo.controller;

import com.example.demo.model.UserMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/rabbit")
public class UserController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostMapping("/send")
    public String sendMessage(@RequestBody UserMessage message) {
        rabbitTemplate.convertAndSend("user-exchange", "user.routingkey", message);
        return "Message sent to RabbitMQ";
    }
}
消费者

创建 UserConsumercom.example.demo.config.UserConsumer):

java 复制代码
package com.example.demo.config;

import com.example.demo.model.UserMessage;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class UserConsumer {

    @RabbitListener(queues = "user-queue")
    public void listen(UserMessage message) {
        System.out.println("Received: ID=" + message.getId() + ", Name=" + message.getName());
    }
}

步骤 5: 运行和测试

  1. 启动消息队列
    • Kafka:docker run -d -p 9092:9092 apache/kafka:3.8.0
    • RabbitMQ:docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management
  2. 运行两个 Spring Boot 项目:mvn spring-boot:run
  3. 测试 API:
    • Kafka :POST http://localhost:8080/api/kafka/send

      json 复制代码
      {
          "id": 1,
          "name": "Alice"
      }

      检查消费者控制台输出。

    • RabbitMQ :POST http://localhost:8081/api/rabbit/send(端口不同):

      json 复制代码
      {
          "id": 1,
          "name": "Alice"
      }

      检查消费者控制台输出。

  4. 验证
    • Kafka:用 kafka-console-consumer.sh 查看 user-topic
    • RabbitMQ:访问 http://localhost:15672(用户/密码:guest/guest)查看队列。

调试技巧

  • 连接失败:检查 application.properties 和服务状态。
  • 消息丢失:确认 topic/queue 名称和序列化配置。
  • 日志:查看 Spring Boot 日志或消息队列日志。

进阶与最佳实践

  • Kafka
    • 分区和副本:增加 partitionsreplicas 提升吞吐和可靠性。
    • 流处理:集成 Kafka Streams 或 Spring Cloud Stream。
    • 监控:使用 Prometheus 和 Grafana 监控 Kafka。
  • RabbitMQ
    • 交换机类型:尝试 Direct、Fanout 或 Headers 交换机。
    • 消息确认:启用 publisher-confirmsconsumer-acknowledgement
    • 集群:配置 RabbitMQ 集群实现高可用。
  • 通用
    • 错误处理:实现重试机制和死信队列。
    • 性能优化:调整消费者并发和批量处理。
    • 资源推荐:Kafka 官网(kafka.apache.org)、RabbitMQ 官网(rabbitmq.com)、《Kafka: The Definitive Guide》。

总结

通过这个示例,你学会了使用 Kafka 和 RabbitMQ 实现消息生产与消费,掌握了 Spring Boot 集成消息队列的基本流程。Kafka 适合高吞吐量场景,RabbitMQ 适合灵活路由。两者各有优势,选择需根据业务需求。

相关推荐
cominglately2 小时前
kafka和rocketmq的副本机制区别: isr 主从模式,Dledger模式
分布式·kafka·rocketmq
qyt19885202 小时前
关于队列的比较(Kafka、RocketMQ、RabbitMQ)
kafka·rabbitmq·rocketmq
渣哥2 小时前
事务没生效还以为成功了?Spring 事务失效的雷区你中招了吗?
java
教游泳的程序员3 小时前
【JDBC】系列文章第一章,怎么在idea中连接数据库,并操作插入数据?
java·ide·mysql·intellij-idea
懒羊羊不懒@3 小时前
C语言指针进阶(进阶)
java·开发语言·面试
nlog3n3 小时前
分布式秒杀系统设计方案
java·分布式
间彧3 小时前
JWT(JSON Web Token)详解
java
前路不黑暗@3 小时前
Java:代码块
java·开发语言·经验分享·笔记·python·学习·学习方法
canonical-entropy3 小时前
NopReport示例-动态Sheet和动态列
java·windows·可逆计算·nop平台·报表引擎