C++使用librdkafka创建消费者和生产者

官方示例

https://github.com/edenhill/librdkafka/blob/master/examples/rdkafka_example.cpp

生产者

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <csignal>
#include <cstring>

#include <librdkafka/rdkafkacpp.h>


static bool run = true;

int main() {

	std::string brokers = "192.168.1.109:9092";
	std::string topic = "WOCAO";

	// 创建全局配置
	RdKafka::Conf* conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL);

	// 错误信息
	std::string errstr;

	// 设置集群
	if (conf->set("bootstrap.servers", brokers, errstr) !=
		RdKafka::Conf::CONF_OK) {
		std::cerr << errstr << std::endl;
		exit(1);
	}

	// 创建消费者实例
	RdKafka::Producer* producer = RdKafka::Producer::create(conf, errstr);
	if (!producer) {
		std::cerr << "Failed to create producer: "
			<< errstr
			<< std::endl;
		exit(1);
	}

	delete conf;

	for (std::string line; run && std::getline(std::cin, line);) {
		if (line.empty()) {
			producer->poll(0);
			continue;
		}

		// 生产者根据主题发布信息
	retry:
		RdKafka::ErrorCode err = producer->produce(
			// 主题
			topic,
			//任何分区:内置分区器将
			//用于将消息分配给基于主题的在消息键上,或没有设定密钥
			RdKafka::Topic::PARTITION_UA,
			// 创建副本?
			RdKafka::Producer::RK_MSG_COPY,
			// 值
			const_cast<char*>(line.c_str()), line.size(),
			// 键
			NULL, 0,
			// 投递时间,默认当前时间
			0,
			// 消息头
			NULL,
			NULL);

		if (err != RdKafka::ERR_NO_ERROR) {
			std::cerr << "% 发布主题错误 " << topic << ": "
				<< RdKafka::err2str(err) << std::endl;

			if (err == RdKafka::ERR__QUEUE_FULL) {
				// 队列已经满了,等待最大十秒
				producer->poll(1000);
				goto retry;
			}

		}
		else {
			std::cerr << "% 队列消息 ("
				<< line.size()
				<< " bytes) "
				<< " 主题: "
				<< topic
				<< std::endl;
		}

		producer->poll(0);
	}

	// 等待消息投递?
	std::cerr << "% Flushing final messages..." << std::endl;
	// 等待十秒
	producer->flush(10 * 1000);

	if (producer->outq_len() > 0) {

		std::cerr << "% "
			<< producer->outq_len()
			<< " 消息未投递"
			<< std::endl;
	}

	delete producer;

	return 0;
}

消费者

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cstring>

#include <librdkafka/rdkafkacpp.h>

// 是否继续消费
static volatile bool run = true;

class ExampleEventCb : public RdKafka::EventCb {
public:
	void event_cb(RdKafka::Event& event) {
		switch (event.type()) {
		case RdKafka::Event::EVENT_ERROR:
			if (event.fatal()) {
				std::cerr << "FATAL ";
				run = false;
			}
			std::cerr << "ERROR (" << RdKafka::err2str(event.err())
				<< "): " << event.str() << std::endl;
			break;

		case RdKafka::Event::EVENT_STATS:
			std::cerr << "\"STATS\": " << event.str() << std::endl;
			break;

		case RdKafka::Event::EVENT_LOG:
			fprintf(stderr, "LOG-%i-%s: %s\n", event.severity(), event.fac().c_str(),
				event.str().c_str());
			break;

		default:
			std::cerr << "EVENT " << event.type() << " ("
				<< RdKafka::err2str(event.err()) << "): " << event.str()
				<< std::endl;
			break;
		}
	}
};

void msg_consume(RdKafka::Message* message, void* opaque) {
	const RdKafka::Headers* headers;

	switch (message->err()) {
	case RdKafka::ERR__TIMED_OUT:
		break;

	case RdKafka::ERR_NO_ERROR:
		// 读取消息
		std::cout << "读取到的消息偏移量" << message->offset() << std::endl;
		if (message->key()) {
			std::cout << "Key: " << *message->key() << std::endl;
		}
		headers = message->headers();
		if (headers) {
			std::vector<RdKafka::Headers::Header> hdrs = headers->get_all();
			for (size_t i = 0; i < hdrs.size(); i++) {
				const RdKafka::Headers::Header hdr = hdrs[i];

				if (hdr.value() != NULL)
					printf(" Header: %s = \"%.*s\"\n", hdr.key().c_str(),
						(int)hdr.value_size(), (const char*)hdr.value());
				else
					printf(" Header:  %s = NULL\n", hdr.key().c_str());
			}
		}
		printf("%.*s\n", static_cast<int>(message->len()),
			static_cast<const char*>(message->payload()));
		break;

	case RdKafka::ERR__PARTITION_EOF:
		// 最后一条信息
		//run = false;
		break;

	case RdKafka::ERR__UNKNOWN_TOPIC:
	case RdKafka::ERR__UNKNOWN_PARTITION:
		std::cerr << "消费错误: " << message->errstr() << std::endl;
		run = false;
		break;

	default:
		/* Errors */
		std::cerr << "消费错误: " << message->errstr() << std::endl;
		run = false;
	}
}


class ExampleConsumeCb : public RdKafka::ConsumeCb {
public:
	void consume_cb(RdKafka::Message& msg, void* opaque) {
		msg_consume(&msg, opaque);
	}
};



int main(int argc, char** argv) {
	// 集群地址
	std::string brokers = "192.168.1.109:9092";
	// 话题
	std::string topic_str = "WOCAO";
	// 分区
	int32_t partition = 0;
	// 开始消费的偏移地址
	int64_t start_offset = RdKafka::Topic::OFFSET_BEGINNING;
	// 错误信息
	std::string errstr;

	// 创建全局配置
	RdKafka::Conf* conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL);
	RdKafka::Conf* tconf = RdKafka::Conf::create(RdKafka::Conf::CONF_TOPIC);

	// 设置集群地址列表
	conf->set("metadata.broker.list", brokers, errstr);

	// 监听事件
	ExampleEventCb ex_event_cb;
	conf->set("event_cb", &ex_event_cb, errstr);

	// 当消费者到达分区结尾,发送 RD_KAFKA_RESP_ERR__PARTITION_EOF 事件
	conf->set("enable.partition.eof", "true", errstr);


	// 创建消费者
	RdKafka::Consumer* consumer = RdKafka::Consumer::create(conf, errstr);
	if (!consumer) {
		std::cerr << "创建消费者失败: " 
			<< errstr 
			<< std::endl;
		exit(1);
	}

	std::cout << "% 消费者名字:" 
		<< consumer->name() 
		<< std::endl;

	// 创建主题指针
	RdKafka::Topic* topic =
		RdKafka::Topic::create(consumer, topic_str, tconf, errstr);
	if (!topic) {
		std::cerr << "创建主题错误: " 
			<< errstr
			<< std::endl;
		exit(1);
	}

	// 开始通过主题、分区、偏移量消费
	RdKafka::ErrorCode resp = consumer->start(topic, partition, start_offset);
	if (resp != RdKafka::ERR_NO_ERROR) {
		std::cerr << "Failed to start consumer: " 
			<< RdKafka::err2str(resp)
			<< std::endl;
		exit(1);
	}

	// 监听回调对象
	ExampleConsumeCb ex_consume_cb;

	// 循环监听信息
	while (run) {
		RdKafka::Message* msg = consumer->consume(topic, partition, 1000);
		msg_consume(msg, NULL);
		delete msg;
		consumer->poll(0);
	}

	// 停止消费
	consumer->stop(topic, partition);

	// 再拉取一次
	consumer->poll(1000);

	delete topic;
	delete consumer;


	delete conf;
	delete tconf;

	// 等待销毁
	RdKafka::wait_destroyed(5000);

	return 0;
}

效果图

QQ截图20220518112832.png

docker创建服务示例

version: '3.7'
services:
  zookeeper:
    image: zookeeper
    ports:
      - "2181:2181"
  kafka:
    image: wurstmeister/kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: 192.168.16.106
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
 #   volumes:
 #     - D:/kafka:/kafka
 #     - D:/kafka/docker.sock:/var/run/docker.sock
  kafka-map:
    image: dushixiang/kafka-map:latest
    ports:
      - 9000:8080
    environment:
      ZK_HOSTS: zookeeper:2181
      DEFAULT_USERNAME: admin
      DEFAULT_PASSWORD: admin
  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
librdkafka 是一个 C/C++ 语言编写的 Apache Kafka 客户端库,它提供了高级别和低级别的 API,使得 Kafka生产者消费者开发变得更加简单和灵活。下面介绍 librdkafka 中的消费者类: librdkafka 中的消费者类是 `RdKafka::Consumer`,它封装了消费者的相关操作。使用该类需要先创建一个 `RdKafka::Conf` 对象来设置消费者的配置,然后创建一个 `RdKafka::KafkaConsumer` 对象来实例化一个消费者。 具体的代码如下: ```c++ RdKafka::Conf *conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL); conf->set("bootstrap.servers", "localhost:9092"); // 设置 Kafka broker 地址 conf->set("group.id", "test-group"); // 设置消费者组 ID std::string errstr; RdKafka::KafkaConsumer *consumer = RdKafka::KafkaConsumer::create(conf, errstr); if (consumer == nullptr) { std::cerr << "Failed to create consumer: " << errstr << std::endl; exit(1); } // 订阅主题 std::vector<std::string> topics = {"test-topic"}; RdKafka::ErrorCode err = consumer->subscribe(topics); if (err != RdKafka::ERR_NO_ERROR) { std::cerr << "Failed to subscribe topic: " << RdKafka::err2str(err) << std::endl; exit(1); } // 开始消费消息 while (true) { RdKafka::Message *msg = consumer->consume(1000); // 每隔 1 秒轮询一次 if (msg == nullptr) { continue; } if (msg->err() == RdKafka::ERR_NO_ERROR) { std::cout << "Received message: " << std::string(static_cast<char *>(msg->payload()), msg->len()) << std::endl; } else { std::cerr << "Failed to consume message: " << RdKafka::err2str(msg->err()) << std::endl; } delete msg; } // 清理资源 consumer->close(); delete consumer; delete conf; ``` 上述代码中,首先创建了一个 `RdKafka::Conf` 对象,并设置了 `bootstrap.servers` 和 `group.id` 两个配置项。然后使用该对象创建一个 `RdKafka::KafkaConsumer` 对象,并订阅了一个名为 `test-topic` 的主题。最后进入消息消费循环,每隔 1 秒钟轮询一次消息,如果有消息到来则打印消息内容。 需要注意的是,消费者使用完毕后需要调用 `close` 方法来清理资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值