【C/C++】基于 Docker 容器运行的 Kafka + C++ 练手项目

文章目录

  • [基于 Docker 容器运行的 Kafka + C++ 练手项目](#基于 Docker 容器运行的 Kafka + C++ 练手项目)
    • [1 项目目的](#1 项目目的)
    • [2 项目框架](#2 项目框架)
    • [3 代码](#3 代码)
    • [4 编译运行](#4 编译运行)
    • [5 功能与接口说明](#5 功能与接口说明)

基于 Docker 容器运行的 Kafka + C++ 练手项目

使用 C++ 语言调用 Kafka 接口的示例项目,通过容器化部署 Kafka + Zookeeper 环境,实现了 Kafka 生产者与消费者的基本功能。

1 项目目的

  • 学习如何用 C++ 操作 Kafka(使用 librdkafka 的 C++ 封装)
  • 实践分布式消息队列的基本使用模式:生产者-消费者
  • 通过 Docker 快速部署 Kafka + Zookeeper 环境
  • 为将来构建中间件(如日志系统、异步任务系统、RPC 框架)奠定基础

2 项目框架

bash 复制代码
cpp-kafka-project/
├── docker-compose.yml # Kafka + Zookeeper + 开发环境容器定义
├── cpp_kafka_code/
│   ├── CMakeLists.txt
│   ├── producer.cpp
│   ├── consumer.cpp
│   └── create_topic.sh # 创建 topic 的脚本

关键技术点

  1. Kafka + Zookeeper 容器化

    • 使用 Confluent 提供的官方镜像:confluentinc/cp-kafkacp-zookeeper

    • 通过 docker-compose.yml 启动三个容器:

      • zookeeper:协调 Kafka Broker
      • kafka:消息代理
      • cpp_dev:Ubuntu 开发容器,内含 C++ 源码和构建环境
  2. Kafka C++ 客户端库

    • 使用 librdkafka 的 C++ 封装接口 rdkafkacpp.h
    • 动态链接 librdkafka++librdkafka
  3. CMake 构建系统

    • 自动查找和链接 Kafka 所需的库与头文件
    • 支持分离构建(out-of-source)

3 代码

docker-compose.yml

yaml 复制代码
version: "3.8"

services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.5.0
    container_name: zookeeper
    ports:
      - "2181:2181"
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000

  kafka:
    image: confluentinc/cp-kafka:7.5.0
    container_name: kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    depends_on:
      - zookeeper

  dev:
    image: ubuntu:22.04
    container_name: cpp_dev
    tty: true
    stdin_open: true
    command: /bin/bash
    working_dir: /home/dev/code
    volumes:
      - ./cpp_kafka_code:/home/dev/code
    depends_on:
      - kafka

create-topic.sh

bash 复制代码
#!/bin/bash

docker exec kafka kafka-topics \
  --create \
  --topic test_topic \
  --bootstrap-server localhost:9092 \
  --partitions 1 \
  --replication-factor 1

加执行权限

bash 复制代码
chmod +x create_topic.sh

CMakeLists.txt

text 复制代码
cmake_minimum_required(VERSION 3.10)
project(cpp_kafka_example)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(PkgConfig REQUIRED)
pkg_check_modules(RDKAFKA REQUIRED IMPORTED_TARGET rdkafka++)

add_executable(producer producer.cpp)
target_link_libraries(producer PkgConfig::RDKAFKA)

add_executable(consumer consumer.cpp)
target_link_libraries(consumer PkgConfig::RDKAFKA)

producer.cpp

cpp 复制代码
#include <librdkafka/rdkafkacpp.h>
#include <iostream>
#include <csignal>
#include <memory>

class ExampleEventCb : public RdKafka::EventCb {
  void event_cb(RdKafka::Event &event) override {
    if (event.type() == RdKafka::Event::EVENT_ERROR) {
      std::cerr << "Kafka Error: " << event.str() << std::endl;
    }
  }
};

int main() {
  std::string brokers = "kafka:9092";
  std::string topic_str = "test_topic";
  std::string errstr;

  // 配置
  ExampleEventCb event_cb;
  std::unique_ptr<RdKafka::Conf> conf(RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL));
  conf->set("bootstrap.servers", brokers, errstr);
  conf->set("event_cb", &event_cb, errstr);

  // 创建 producer
  std::unique_ptr<RdKafka::Producer> producer(RdKafka::Producer::create(conf.get(), errstr));
  if (!producer) {
    std::cerr << "Failed to create producer: " << errstr << std::endl;
    return 1;
  }

  // 创建 Topic
  std::unique_ptr<RdKafka::Topic> topic(RdKafka::Topic::create(producer.get(), topic_str, nullptr, errstr));
  if (!topic) {
    std::cerr << "Failed to create topic: " << errstr << std::endl;
    return 1;
  }

  std::string message = "Hello from C++ Kafka Producer!";
  RdKafka::ErrorCode resp = producer->produce(
    topic.get(),                            // topic ptr
    RdKafka::Topic::PARTITION_UA,           // partition
    RdKafka::Producer::RK_MSG_COPY,         // message flags
    const_cast<char *>(message.c_str()),    // payload
    message.size(),                         // payload size
    nullptr,                                // optional key
    nullptr);                               // opaque

  if (resp != RdKafka::ERR_NO_ERROR) {
    std::cerr << "Produce failed: " << RdKafka::err2str(resp) << std::endl;
  } else {
    std::cout << "Message sent successfully\n";
  }

  producer->flush(3000);
  return 0;
}

consumer.cpp

cpp 复制代码
#include <librdkafka/rdkafkacpp.h>
#include <iostream>
#include <csignal>
#include <memory>

bool running = true;

void signal_handler(int) {
  running = false;
}

class ExampleEventCb : public RdKafka::EventCb {
  void event_cb(RdKafka::Event &event) override {
    if (event.type() == RdKafka::Event::EVENT_ERROR) {
      std::cerr << "Kafka Error: " << event.str() << std::endl;
    }
  }
};

int main() {
  signal(SIGINT, signal_handler);

  std::string brokers = "kafka:9092";
  std::string topic = "test_topic";
  std::string group_id = "cpp_consumer_group";

  std::string errstr;
  ExampleEventCb event_cb;

  auto conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL);
  conf->set("bootstrap.servers", brokers, errstr);
  conf->set("group.id", group_id, errstr);
  conf->set("auto.offset.reset", "earliest", errstr);
  conf->set("event_cb", &event_cb, errstr);

  auto consumer = RdKafka::KafkaConsumer::create(conf, errstr);
  if (!consumer) {
    std::cerr << "Failed to create consumer: " << errstr << std::endl;
    return 1;
  }

  consumer->subscribe({topic});
  std::cout << "Consuming messages from topic " << topic << std::endl;

  while (running) {
    auto msg = consumer->consume(1000);
    if (msg->err() == RdKafka::ERR_NO_ERROR) {
      std::string message(reinterpret_cast<const char*>(msg->payload()), msg->len());
		std::cout << "Received message: " << message << std::endl;
    }
    }
    delete msg;
  }

  consumer->close();
  delete consumer;
  return 0;
}

4 编译运行

  1. 启动容器
bash 复制代码
# docker-compose.yml所在目录下
docker-compose up -d
  1. 安装依赖并编译
bash 复制代码
docker exec -it cpp_dev /bin/bash
# 以下在容器内执行
apt update && apt install -y g++ cmake pkg-config librdkafka-dev librdkafka++1
mkdir -p build && cd build
cmake ..
make
  1. 创建kafka topic:
bash 复制代码
# 宿主机下
./create_topic.sh
  1. cpp_dev容器下运行consumer和producer
bash 复制代码
./consumer &
./producer

输出

bash 复制代码
/home/dev/code/build# ./consumer &
[1] 4069
/home/dev/code/build# Consuming messages from topic test_topic

/home/dev/code/build# ./producer 
Message sent successfully
Received message: Hello from C++ Kafka Producer!

5 功能与接口说明

5.1 Producer 接口:producer.cpp

功能:向指定的 topic(如 test_topic)持续发送消息。

关键调用流程
cpp 复制代码
RdKafka::Conf::create(...)                     // 创建配置对象
conf->set(...)                                 // 设置 broker 等参数
RdKafka::Producer::create(...)                 // 创建 Producer 实例
producer->produce(...)                         // 发送消息
参数说明
参数 说明
topic 目标 topic 名称
partition 使用 RdKafka::Topic::PARTITION_UA 表示由 Kafka 自动分配
message flags 通常为 RK_MSG_COPY
payload 消息数据(char*)
payload length 消息长度(size_t)

5.2 Consumer 接口:consumer.cpp

功能:从指定的 topic 订阅并消费消息。

关键调用流程
cpp 复制代码
RdKafka::Conf::create(...)                     // 创建全局配置
conf->set(...)                                 // 设置 group.id 等参数
RdKafka::KafkaConsumer::create(...)            // 创建 KafkaConsumer 实例
consumer->subscribe(...)                       // 订阅 topic
consumer->consume(...)                         // 拉取消息
消费流程中注意
  • msg->payload() 需要转换为 char* 后构造成字符串打印
  • 使用 msg->err() 判断是否正常收到消息

5.3 工程技术点

技术点 描述
容器部署 无需本机安装 Kafka,快速启动测试环境
Kafka 消费模型 使用 KafkaConsumer 拉模式消费,便于理解
CMake 模块化 可轻松扩展更多模块(如 logger、metrics)
中间件模板 可作为日志系统、消息队列、调度中心等中间件的原型
相关推荐
快乐的阿常艾念宝25 分钟前
如何从ISO镜像直接制作Docker容器基础镜像
docker·iso镜像·基础镜像·制作基础镜像
maxruan1 小时前
docker环境添加安装包持久性更新
运维·docker·容器
云泽8082 小时前
模块化设计,static和extern(面试题常见)
c语言·面试·职场和发展
Hoking2 小时前
CentOS7.9环境离线部署docker和docker-compose的两种方式
git·docker·容器
dd向上3 小时前
8位单通道数据保存为JPG
c++·图像处理
蓉哈哈4 小时前
【Docker 从入门到实战全攻略(一):核心概念 + 命令详解 + 部署案例】
运维·docker·容器
沙滩小岛小木屋4 小时前
docker中多个容器相互访问的端口问题
运维·docker·容器
fpcc4 小时前
跟我学c++中级篇——动态库的资源处理
开发语言·c++
才鲸嵌入式4 小时前
Windows10下使用QEMU安装Ubuntu20.04虚拟机,并启用硬件加速
c语言·ubuntu·嵌入式·qemu·虚拟机·模拟器
泽02024 小时前
C++之string的模拟实现
开发语言·数据结构·c++·算法