kafka是一个分布式流处理的平台,通过kafka我们可以发布和订阅流式记录。有关kafka的介绍可以参考官网或者这篇文章https://juejin.im/post/6844903495670169607,介绍的非常的详细。
我个人喜欢把kafka当做数据库去理解,因为它也有存放数据的功能,但是还是与数据库是不一样的。
kafka中多个broker称为一个集群,一个broker(独立的kafka服务器)中可以创建不同的topic(主题),topic下可以建立多个partition(分区),数据则存放在分区中的一个单元里面(partition可以近似理解为一个数组,存入kafka的一条记录就存放在partition1[0]、partition1[1]...)。
生产者向kafka中生产数据,即把数据推入kafka中,消费者消费kafka中的数据,一个主题可以被多个消费者消费,这个是不同于数据库的地方。
kafka结构图.jpg
下面介绍kafka在Linux下的简单使用
1 使用流程
2 问题及解决办法
3 代码示例
1 使用流程
首先服务器上必须已经安装好kafka!
1、在GitHub上下载librdkafka-master.zip安装文件,解压到文件夹https://github.com/edenhill/librdkafka
2、编译文件
cd librdkafka-master //cd到刚刚解压到的文件目录下
chmod 777 configure lds-gen.py
./confgure
make
make install
3、新建文件夹用于存放代码
4、在搭建好的kafka服务器上新建主题"test1"
./kafka-topics.sh --create --zookeeper localhost:2198--replication-factor1 --partitions1 --topic test1
2193是kafka所在的端口,根据自己的实际情况设置 。
5、将/usr/local/lib路径下的librdkafka++.a、librdkafka++.so、librdkafka.so.1放入自己创建的代码的文件夹下;
6、rdkafkacpp.h头文件加入创建的代码文件夹下,并在代码中引入头文件#include "rdkafkacpp.h"
7、在代码中设置broker和topic
std::string brokers="127.0.0.1:9092"; //地址为kafka服务器地址,这里是随便写的
std::string topic="test1" //主题为kafka中存在的主题
8、编译代码时加入 -lstdc++ -lrdkafka -lrdkafka++ -lrt -lpthread -L.
2 问题及解决办法
1、Connection refused
thrd:localhost:9092/bootstrap: Connection to ipv6#[::1]: 9092 failed : Connection refused
此类问题是由于地址设置不正确,没有将brokers地址设置为kafka所在的服务器地址,且服务器地址不能使用localhost代替。
2、搭建的kafka上查看不到创建的主题
可以查看rdkafkacpp.h头文件,调用的函数是哪一个,有的函数里的参数里面的topic主题必须是kafka上已经存在的主题。
3、Disconnected While requesting ApiVersion
Disconnected While requesting ApiVersion: might be caused by incorrect security. protocol configuration (connecting to a SSL listener?) or broker version is < 0.10
这个问题是由于代码中brokers的服务端口设置错误导致,应该设置为9092(配置文件中的默认端口):
std::string brokers = "ip::9092";
4、代码存放的位置不一定与kafka在同一位置上,只要代码中的brokers的地址设置为kafka所在的服务器地址即可。
代码示例
这个是参考github上面的代码改的producer.cpp
想要把代码运行起来,需要参考上面的 1 使用流程 进行设置
···
#include
#include
#include
#include
#include
#include
#if _AIX
#include
#endif
#include "rdkafkacpp.h"
static volatile sig_atomic_t run = 1;
static void sigterm (int sig) {
run = 0;
}
//成功发送一次信息调用一次回调函数
class ExampleDeliveryReportCb : public RdKafka::DeliveryReportCb {
public:
void dr_cb (RdKafka::Message &message) {
/* If message.err() is non-zero the message delivery failed permanently for the message. */
if (message.err())
std::cerr << "% Message delivery failed: " << message.errstr() << std::endl;
else
std::cerr << "% Message delivered to topic " << message.topic_name() <<
" [" << message.partition() << "] at offset " <<
message.offset() << std::endl;
}
};
int main () {
std::string brokers = "127.0.0.1:9092"; //这里的IP地址应该设置为kafka所在服务器地址
std::string topic = "test"; //设置主题
std::string errstr;
//设置配置
RdKafka::Conf *conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL);
if (conf->set("bootstrap.servers", brokers, errstr) !=
RdKafka::Conf::CONF_OK) {
std::cerr << errstr << std::endl;
exit(1);
}
signal(SIGINT, sigterm);
signal(SIGTERM, sigterm);
ExampleDeliveryReportCb ex_dr_cb;
if (conf->set("dr_cb", &ex_dr_cb, errstr) != RdKafka::Conf::CONF_OK) {
std::cerr << errstr << std::endl;
exit(1);
}
//创建 producer
RdKafka::Producer *producer = RdKafka::Producer::create(conf, errstr);
if (!producer) {
std::cerr << "Failed to create producer: " << errstr << std::endl;
exit(1);
}
delete conf;
/*
* Read messages from stdin and produce to broker.
*/
std::cout << "% Type message value and hit enter " <<
"to produce message." << std::endl;
for (std::string line; run && std::getline(std::cin, line);) {
if (line.empty()) {
producer->poll(0);
continue;
}
//将message传入队列中
retry:
RdKafka::ErrorCode err =
producer->produce(topic,
RdKafka::Topic::PARTITION_UA, //随机分区
/* Make a copy of the value */
RdKafka::Producer::RK_MSG_COPY /* Copy payload */,
/* Value */
const_cast(line.c_str()), line.size(), //需要写入的数据
/* Key */
NULL, 0,
/* Timestamp (defaults to current time) */
0,
/* Message headers, if any */
NULL,
/* Per-message opaque value passed to
* delivery report */
NULL);
if (err != RdKafka::ERR_NO_ERROR) {
std::cerr << "% Failed to produce to topic " << topic << ": " <<
RdKafka::err2str(err) << std::endl;
if (err == RdKafka::ERR__QUEUE_FULL) { //存放数据的队列已满,将执行等待
producer->poll(1000);
goto retry;
}
} else {
std::cerr << "% Enqueued message (" << line.size() << " bytes) " <<
"for topic " << topic << std::endl;
}
producer->poll(0); //保持常态
}
std::cerr << "% Flushing final messages..." << std::endl;
producer->flush(10*1000 /* wait for max 10 seconds */);
run = true ; //之前因为没有加这个就报错了
if (producer->outq_len() > 0) //发送数据
std::cerr << "% " << producer->outq_len() <<
" message(s) were not delivered" << std::endl;
delete producer;
return 0;
}
...