ApacheKafka是一个分布式流媒体平台,这到底是什么意思呢?接下来我们看一下流媒体平台有三个关键功能如下:
第一:发布和订阅记录流,类似于消息队列或企业消息传递系统。
第二:以容错持久的方式存储记录流。
第三:处理记录发生的流。
我们可以将Kafka理解成一个总线,任何节点都可以将记录流发布到总线上,任何节点也都可以订阅记录流,订阅时会通过Topic区分。
kafka起先由领英(linkedin创建)公司,开源后被Apache基金会纳入子项目。我们在下载Kafka时,你是如何区分它的版本呢?比如本篇博客下载kafka的版本是“kafka_2.11-1.1.0”,这个“2.11”是scala(java语言脚本化)版本而“1.1.0”是kafka版本。
所需资源:
1)linux机器(我的环境是linux,而且linux更方便)
2)hadoop服务
3)zookeeper服务
4)kafka服务
上文已经介绍,Kafka目前属于hadoop生态,那么要使用Kafka,我们必须先启动hadoop,同时Kafka依赖zookeeper,我们还必须启动zookeeper服务。
我们的目的是测试kafka服务,并理解机制,因此简单的单机配置就可以了,生产环境自然会有后台同事搭建大型集群。
hadoop的详细搭建配置步骤在此不再赘述,可以参看 Hadoop(二)搭建伪分布式集群 这篇blog,我认为写得非常清晰。
大体顺序是:
1)下载jdk并解压
2)修改系统环境配置(添加jdk路径)
3)下载hadoop并解压
4)修改系统环境配置(添加hadoop路径)
5)修改hadoop配置文件(配置ip等)
6)启动hadoop并测试
同样,我们还需要配置单机的zookeeper,可以参看 安装zookeeper(单机,伪集群) 进行下载配置。
本来也想甩几个优质blog的,但发现都是各讲一块,不够系统,所以亲自归纳。
下载地址 https://www.apache.org/dyn/closer.cgi?path=/kafka/1.1.0/kafka_2.11-1.1.0.tgz
下载完后上传到linux服务器解压
tar -zxvf kafka_2.11-1.1.0.tgz /opt/
# 做软链接,方便记录地址和修改版本
ln -s /opt/kafka_2.11-1.1.0/ /opt/kafka
编辑/etc/profile文件,添加如下信息:
#ADD KafKa PATH
export KAFKA_HOME=/opt/kafka
PATH=$PATH:$KAFKA_HOME/bin
并执行source /etc/profile使配置生效。
这里我们简单地使用sed -i命令添加信息:
sed -i 's@#listeners=PLAINTEXT://:9092@listeners=PLAINTEXT://s101:9092@g' /opt/kafka/config/server.properties
sed -i '[email protected]=/tmp/[email protected]=/home/yinzhengjie/kafka/logs@g' /opt/kafka/config/server.properties
sed -i '[email protected]=localhost:[email protected]=s102:2181,s103:2181,s104:2181@g' /opt/kafka/config/server.properties
当然也可以打开配置文件/opt/kafka/config/server.properties手动添加。
执行脚本启动服务(加上-daemon 让其后台运行),然后用jps命令可以看到Kafka已经启动。
通过netstat -untalp | grep 9092命令查看kafka服务的ip,
[root@com bin]$ /opt/kafka/bin/kafka-server-start.sh -daemon /opt/kafka/config/server.properties
[root@com bin]$ jps
18608 Application
29232 Kafka
20721 NodeManager
22258 QuorumPeerMain
20535 DataNode
10440 jar
28682 Application
29292 Jps
[root@com bin]$ netstat -untalp | grep 9092
tcp 0 0 10.25.77.54:35914 30.23.9.175:9092 ESTABLISHED 28682/java
从命令行输出我们得到ip为30.23.9.175,使用以下命令建立生产者。
“–topic”后面填写topic名称,接收方将通过topic名称找到要接收的数据流。
[root@com bin]$ kafka-console-producer.sh --broker-list 30.23.9.175:9092 --topic test_20190516
>
接下来我们发现都在一个对话环境交互,输入要发送的文字:
>
>
>can you hear me?
>
输入如下命令启动消费者,将通过topic监听数据流,启动后马上就会受到刚刚发出的文字。
[root@com bin]$ kafka-console-consumer.sh --bootstrap-server 30.23.9.175:9092 --topic test_20190516 --from-beginning
can you hear me?
到这里还没完,命令行的操作我们都熟悉了,但是在开发中我们经常需要在模型工程中和外部数据源交互,那么如何用python实现接收到发送kafka数据呢。
1)安装环境
使用anaconda环境,在prompt中输入pip install kafka即可完成安装。
2)发送消息
使用KafkaProducer发送消息
from kafka import KafkaProducer
kafka_host='30.23.9.175' # host
kafka_port=9092 # port
kafka_topic='test_20190516'
producer = KafkaProducer(
bootstrap_servers=['{kafka_host}:{kafka_port}'.format(kafka_host=kafka_host, kafka_port=kafka_port)]
)
message_string = 'can you hear me?'
response = producer.send(kafka_topic, message_string.encode('utf-8'))
3)接收消息
使用KafkaComsuer接收消息
from kafka import KafkaConsumer
consumer = KafkaConsumer(
'test_20190516',
bootstrap_servers=['{kafka_host}:{kafka_port}'.format(kafka_host=kafka_host, kafka_port=kafka_port)]
)
for message in consumer:
print(type(message.value), message.value, message)
# ...
consumer可迭代,当队列中没有消息时,上面代码会一直等待。使用Control+C可以退出循环。
这样将接收到如下信息:
(<type 'str'>, 'can you hear me?', ConsumerRecord(topic=u'test_20190516', partition=0, offset=6, timestamp=1557997768959, timestamp_type=0, key=None, value='can you hear me?', checksum=658729517, serialized_key_size=-1, serialized_value_size=16))
观察发现,message是一个类的实例化对象,message.value可以取出数据信息。
临近结束,有一点不得不提,就是Groupid这个参数,我们在关于Kafka的blog中经常看到Groupid这个参数的使用,但是这个到底干嘛用的?都讲的模棱两可。今天一定要摆事实讲道理列清楚。
1)Groupid是Kafka consumer的参数,不是Kafka producer的参数。
2)Groupid不是用来让Kafka consumer筛选Kafka producer的内容的,筛选内容是用Topic这个参数。
3)Groupid只是用于保证Kafka consumer得到的数据唯一且连续,不会因为接受端的冲突导致收到多份重复数据,这个怎么理解呢,下面摆个例子。
第一步!
借用上文的变量继续工作,我们使用Kafka producer发送字符串“No.1”:
>>> # Kafka producer
>>> response = producer.send(kafka_topic, 'No.1 ...')
同时准备两个Kafka consumer接收消息,Groupid分别为“group1”和“group2”,
“group1”得到的消息是:
ConsumerRecord(topic=u'test_20190516', partition=0, offset=16, timestamp=1559891940660, timestamp_type=0, key=None, value='No.1 ...', checksum=1447245960, serialized_key_size=-1, serialized_value_size=8)
“group2”得到的消息是:
ConsumerRecord(topic=u'test_20190516', partition=0, offset=16, timestamp=1559891940660, timestamp_type=0, key=None, value='No.1 ...', checksum=1447245960, serialized_key_size=-1, serialized_value_size=8)
两个完全一样对不对?好,下面继续。
第二步
使用Kafka producer发送字符串“No.2”:
>>> # Kafka producer
>>> response = producer.send(kafka_topic, 'No.1 ...')
同时再多准备一个Kafka consumer接收消息,Groupid为“group1”,注意,现在有三个consumer,Groupid分别为“group1”和“group2”和“group1”,我们分别按序号1、2、3表示:
1号consumer得到的新消息是空的!没有新消息:
2号consumer得到的消息是:
ConsumerRecord(topic=u'test_20190516', partition=0, offset=17, timestamp=1559893332851, timestamp_type=0, key=None, value='No.2 ...', checksum=-915836631, serialized_key_size=-1, serialized_value_size=8)
3号consumer得到的消息是:
ConsumerRecord(topic=u'test_20190516', partition=0, offset=17, timestamp=1559893332851, timestamp_type=0, key=None, value='No.2 ...', checksum=-915836631, serialized_key_size=-1, serialized_value_size=8)
总结
注意到没有,由于Groupid一致,3号consumer继续收到producer的消息,而1号就收不到了,保证了这个Groupid无论在什么端接收都是唯一且连续的。
如果出现kafka.errors.NoBrokersAvailable: NoBrokersAvailable的报错,一般是因为Kafka服务没有正常启动。
[1] https://kafka-python.readthedocs.io/en/master/index.html
[2] https://www.runoob.com/linux/linux-comm-netstat.html
[3] https://www.cnblogs.com/yinzhengjie/p/9209058.html
[4] https://blog.csdn.net/mingyunxiaohai/article/details/80613227
[5] https://www.cnblogs.com/yinzhengjie/p/9209319.html