前言
Kafka,作为目前在大数据领域应用最为广泛的消息队列,其内部实现和设计有很多值得深入研究和分析的地方。
使用kafka首先需要接触到producer的开发,然后是consumer开发。
下面进行producer的解析。
Producer概要设计
发送简略流程图
流程如下:
<1>KafkaProducer首先使用序列化器将需要发送的数据进行序列化,
<2>然后通过分区器(partitioner)确定该数据需要发送的Topic的分区,
kafka提供了一个默认的分区器,
如果消息指定了key,那么会根据key的hash(哈希)值来确定目标分区,
如果没有指定key,那么将使用`轮询`的方式确定目标分区,
这样可以最大程度的均衡每个分区的消息负载,
确定分区之后,将会进一步确认该分区的leader节点(处理该分区消息读写的主节点),
消息会进入缓冲池进行缓冲,然后等消息到达一定数量或者大小后进行批量发送
同步/异步发送消息
<1>同步发送消息
优点:可以保证每条消息准确无误的写入了broker,
对于立即需要发送结果的情况非常适用,
在producer故障或者宕机的情况也可以保证结果的正确性。
可以避免消息丢失。
缺点:由于同步发送需要每条消息都需要及时发送到broker,
没有缓冲批量操作,性能较低,吞吐量小。
<2>异步发送消息
优点:可以通过缓冲池对消息进行缓冲,然后进行消息的批量发送,
大量减少了和broker的交互频率,性能极高,可以通过回调机制获取发送结果.
吞吐量大。
缺点:在producer直接断电或者重启等故障,将有可能丢失消息发送结果,
对消息准确性要求很高的场景不适用
producer使用非常简单
1.初始化生产者对象,配置KafkaProducer()类的参数
producer = KafkaProducer(bootstrap_servers=['host1:9092','host2:port'])
2.构造ProducerRecord消息
3.调用send方法进行发送
4.最后关闭producer资源
消息如何分区partition
生产者在在往kafka消息队列发送(send)消息的时候,可以指定topic。
那么如何指定分区呢?
<1>消息分区策略:
关于 partition 值的计算,分为三种情况:
1)指明 partition 的情况下,直接将指定的值直接作为 partiton 值;
但是客户端在指定分区信息时需要考虑数据均衡问题。
2)没有指明 partition 值但有 key 的情况下,
将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值;
3)既没有 partition 值又没有 key 值的情况下,
第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),
将这个值与 topic 可用的 partition 总数取余得到 partition 值,
也就是常说的轮询算法。
<2>自定义分区策略
可以通过实现org.apache.kafka.clients.producer.Partitioner自定分区策略,
在构造KafkaProducer是配置参数partitioner.class为自定义的分区类即可
单线程代码实例
from kafka import KafkaProducer
producer = KafkaProducer(bootstrap_servers=['host1:9092','host2:port'])
for i in range(3):
msg = "msg%d" % i
producer.send('topic_1',msg)
producer.close()
多线程代码实例
import random
import time
import threading
from kafka import KafkaProducer
from traitlets import log
def on_send_success(record_metadata):
print(record_metadata.topic)
print(record_metadata.partition)
print(record_metadata.offset)
def on_send_error(excp):
log.error('I am an errback',exc_info=excp)
def send_messag(topic,id,interval_time):
producer = KafkaProducer(bootstrap_servers=['cdh01:9092',
'cdh02:9092',
'cdh03:9092'])
while True:
startTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
message = "|".join((id,startTime,str(interval_time)))
producer.send(topic,message.encode("utf-8")).add_callback(on_send_success).add_errback(on_send_error)
time.sleep(interval_time)
interval_times = [60,300,600,1200,1800,3600]
for i in range(0,50):
deviceId = "%06d" % random.randint(0,999999)
interval_time = times[random.randint(0, len(interval_times) - 1)]
recv_thread = threading.Thread(target=send_messag, args=("my-topic", id, interval_time))
recv_thread.setDaemon(True)
recv_thread.start()
while True:
time.sleep(5000)