librdkafka C++ 实现手动提交offset,一条一条的消费

文章讲述了如何在使用librdkafka1.6.1时,手动控制ApacheKafka消费者的offset提交,重点强调了禁用自动提交和相关配置参数的设置过程。
摘要由CSDN通过智能技术生成

最近搞了一个东西,需要一条一条的消费kafka中的内容,处理完成之后提交offset。不能自动提交。于是在网上找了很多资料,包括询问AI。结果都不尽人意。

于是在总结了多方网络上的资料和AI及身边人的提醒之后,总结了一个实例如下。

我当时使用的版本是librdkafka1.6.1

重点

需要配置如下四个参数:

 // 设置是否需要自动提交- 这里为false, 这一点非常重要。高阶的kafkaconsumer的设置在 global conf
    // 低阶的在topic conf.
	if(_pConf->set("enable.auto.commit", "false", _errstr) != RdKafka::Conf::CONF_OK)
	{
		printf("set enable.auto.commit failed %s \n",_errstr.c_str());
		return false;
	}
    // 设置是否自动存储offset - 这里为false 这一点非常重要
	if(_pConf->set("enable.auto.offset.store", "false", _errstr) != RdKafka::Conf::CONF_OK)
	{
		printf("set enable.auto.offset.store failed %s\n",_errstr.c_str());
		return false;
	}
    // 设置每次拉取1条消息 - 重要 -这里保证了你是一条一条的拉取
	if(_pConf->set("max.poll.records", "1", _errstr) != RdKafka::Conf::CONF_OK)
    {
        printf("set max.poll.records failed %s\n",_errstr.c_str());
		return false;
    }
    // 设置消费者拉取消息的最大等待时间为30秒 - 这个根据自己的时间来定
  	if(_pConf->set("fetch.max.wait.ms", "30000", _errstr) != RdKafka::Conf::CONF_OK)
    {
        printf("set fetch.max.wait.ms failed %s\n",_errstr.c_str());
		return false;
    }

完整的代码如下

#include "rdkafkacpp.h"
#include <string>
#include <iostream>
#include <vector>
using namespace std;

/** 需要设置的东西我放在了外面**/
string _brokers = "192.168.12.23:9092,192.168.12.24:9092,192.168.12.25:9092"; // kafka brokers
string _groupID = "test_group_1"; // groupID
string _errstr;       // 用来输出错误信息
string _topicStr = "TEST_TOPIC_XYQ";     // topic 可以弄多个
std::vector<std::string> _vtopics;	// kafka消费者需要的topics的容器
RdKafka::Conf * _pConf = NULL;
RdKafka::KafkaConsumer*_consumer = NULL;//rdkafka 消费者 hight level , 支持groupid

// 当有错误发生时就重启kafka消费者
static bool _gRun = true;
class CEventCb : public RdKafka::EventCb {
public:
	void event_cb(RdKafka::Event &event) {
		switch (event.type())
		{
		case RdKafka::Event::EVENT_ERROR:
			//LOG(ERROR) << "KafkaConsumer Event error " << event.str();
            printf("KafkaConsumer Event error :%s\n",event.str().c_str());
			_gRun = false;
			break;

		case RdKafka::Event::EVENT_STATS:
			//LOG(ERROR) << "KafkaConsumer stats error " << event.str();
            printf("KafkaConsumer Event stats :%s\n",event.str().c_str());
			break;

		case RdKafka::Event::EVENT_LOG:
			//LOG(ERROR) << "KafkaConsumer stats error " << event.fac() << "--" << event.str();
            printf("KafkaConsumer Event LOG  : %s --%s\n",event.fac().c_str() ,event.str().c_str());
			break;

		default:
			break;
		}
	}
};


bool InitKafka()
{
    CEventCb _ex_event_cb;// 事件回调
    // init kafka params
    _pConf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL); // kafka GLOBAL
	RdKafka::Conf * _pTConf = RdKafka::Conf::create(RdKafka::Conf::CONF_TOPIC); // kafka TOPIC

    // 配置brokers
    if(_pConf->set("metadata.broker.list", _brokers, _errstr) !=  RdKafka::Conf::CONF_OK){
        printf("set metadata.broker.list failed %s \n",_errstr.c_str());
        return false;
    }
     // 配置回调
	if(_pConf->set("event_cb",&_ex_event_cb,_errstr)!= RdKafka::Conf::CONF_OK){
        printf("set event_cb failed %s \n",_errstr.c_str());
        return false;
    }
    // 配置groupID
    if(_pConf->set("group.id", _groupID, _errstr) != RdKafka::Conf::CONF_OK){
		printf("set group.id failed %s \n",_errstr.c_str());
		return false;
	}
    // 设置是否需要自动提交- 这里为false, 这一点非常重要。高阶的kafkaconsumer的设置在 global conf
    // 低阶的在topic conf.
	if(_pConf->set("enable.auto.commit", "false", _errstr) != RdKafka::Conf::CONF_OK){
		printf("set enable.auto.commit failed %s \n",_errstr.c_str());
		return false;
	}
    // 设置是否自动存储offset - 这里为false 这一点非常重要
	if(_pConf->set("enable.auto.offset.store", "false", _errstr) != RdKafka::Conf::CONF_OK){
		printf("set enable.auto.offset.store failed %s\n",_errstr.c_str());
		return false;
	}
    // 设置每次拉取1条消息 - 重要 -这里保证了你是一条一条的拉取
	if(_pConf->set("max.poll.records", "1", _errstr) != RdKafka::Conf::CONF_OK){
        printf("set max.poll.records failed %s\n",_errstr.c_str());
		return false;
    }
    // 设置消费者拉取消息的最大等待时间为30秒 - 这个根据自己的时间来定
  	if(_pConf->set("fetch.max.wait.ms", "30000", _errstr) != RdKafka::Conf::CONF_OK){
        printf("set fetch.max.wait.ms failed %s\n",_errstr.c_str());
		return false;
    }
    // 是否允许partition 消费到末尾的提示,这个不重要,只是为了让你知道某个partition消费到末尾了
    //conf->set("enable.partition.eof", "true", errstr);

    // 默认topic设置
	if(_pConf->set("default_topic_conf", _pTConf, _errstr) != RdKafka::Conf::CONF_OK){
		printf("set default_topic_conf failed %s \n",_errstr.c_str());
        return false;
	}
    // 用完了就删了
	delete _pTConf;
	_pTConf  = NULL;    
    return true;
}
void msgConsume(RdKafka::Message* message, void* opaque)
{
    if(message == NULL){
        return ;
    }
    int errcode = message->err();
    if (errcode == RdKafka::ERR_NO_ERROR)  //消费数据
    {
        const char* ptr = static_cast<const char*>(message->payload());
		int lenn = static_cast<int>(message->len());
        string str(ptr,lenn);
		if(lenn > 0){
			printf("topic:%s,partition:%d,msg:%s \n",message->topic_name().c_str(), message->partition(),str.c_str());
            // do something // 在这要怎么处理随便了。
        }

    }else if(errcode == RdKafka::ERR__TIMED_OUT){ // 超时了
        printf("consume time out \n");
    }else if(errcode == RdKafka::ERR__PARTITION_EOF){
        // 消费到了某个partion的末尾,不用管,大部分时间我们不关系这个
        printf("consume at partition end .\n");
    }else{
        // 这里是真正消费到什么错误了,然后把_gRun 弄成false .让程序退出了。
        printf("consume failed. ERROR: %s .\n",message->errstr().c_str() );
        _gRun = false;
    }
}
int main(){

    // init kafka params
    bool bInitResult = InitKafka();
    if(!bInitResult){
        printf("Init kafka failed .\n");
        return -1;
    }
    /*
     * Create consumer using accumulated global configuration.
     * 创建consumer, 使用你之前配置好的全局conf
     */
    _consumer = RdKafka::KafkaConsumer::create(_pConf, _errstr);
	if (!_consumer){
        std::cerr << "Failed to create consumer: " << _errstr << std::endl;
		return -1;
	}

    std::cout << "% Created consumer " << _consumer->name() << std::endl;
	//create topic
	/*
   	 * Subscribe to topics
   	 */
	_vtopics.push_back(_topicStr);		// topic1
	RdKafka::ErrorCode err = _consumer->subscribe(_vtopics);
	if (err){
		std::cout << "% Csubscribe topic failed:  " << RdKafka::err2str(err) << std::endl;
		return -1;
	}


    while(_gRun){
        RdKafka::Message *pMsg = _consumer->consume(5000);// 消费 (这里我的超时时间是5秒,根据自己的情况来定)
        // 处理流程
		msgConsume(pMsg,NULL);
        // 提交offset
        _consumer->commitSync(pMsg);
        // 回收内存
        if(pMsg != NULL){
			delete pMsg;
			pMsg = NULL;
		}
    }
    // 再提交一次-当有问题发生时
	err = _consumer->commitSync();
    std::cerr << "sync commit returned " << RdKafka::err2str(err) << std::endl;

    // 关闭消费者
    if(_consumer  != NULL){
		_consumer->close();		// 关闭消费者
        _consumer->poll(1000);
		delete _consumer;
        _consumer = NULL;
	}

    if(_pConf != NULL){
        delete _pConf;
        _pConf = NULL;
    }
    /*
   * Wait for RdKafka to decommission.
   * This is not strictly needed (when check outq_len() above), but
   * allows RdKafka to clean up all its resources before the application
   * exits so that memory profilers such as valgrind wont complain about
   * memory leaks.
   */
	RdKafka::wait_destroyed(5000);

    return 0;
}

// 我自己编译了 librdkafka1.6.1 然后把 下面四个文件拷贝出来放到了 /root/rdkafka 下面
// librdkafka.a  librdkafka++.a  rdkafkacpp.h  rdkafka.h 
//g++ -std=c++11 -I/root/rdkafka -L/root/rdkafka  -L//usr/local/lib testkafkaconsumerManual.cpp -lrdkafka++ -lrdkafka   -lsasl2 -lssl -lcrypto -lrt -lz

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值