深入了解如何基于Python读写Kafka


Posted in Python onDecember 31, 2019

这篇文章主要介绍了深入了解如何基于Python读写Kafka,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

本篇会给出如何使用python来读写kafka, 包含生产者和消费者.

以下使用kafka-python客户端

生产者

爬虫大多时候作为消息的发送端, 在消息发出去后最好能记录消息被发送到了哪个分区, offset是多少, 这些记录在很多情况下可以帮助快速定位问题, 所以需要在send方法后加入callback函数, 包括成功和失败的处理

# -*- coding: utf-8 -*-

'''
callback也是保证分区有序的, 比如2条消息, a先发送, b后发送, 对于同一个分区, 那么会先回调a的callback, 再回调b的callback
'''

import json
from kafka import KafkaProducer

topic = 'demo'


def on_send_success(record_metadata):
  print(record_metadata.topic)
  print(record_metadata.partition)
  print(record_metadata.offset)


def on_send_error(excp):
  print('I am an errback: {}'.format(excp))


def main():
  producer = KafkaProducer(
    bootstrap_servers='localhost:9092'
  )
  producer.send(topic, value=b'{"test_msg":"hello world"}').add_callback(on_send_success).add_callback(
    on_send_error)
  # close() 方法会阻塞等待之前所有的发送请求完成后再关闭 KafkaProducer
  producer.close()


def main2():
  '''
  发送json格式消息
  :return:
  '''
  producer = KafkaProducer(
    bootstrap_servers='localhost:9092',
    value_serializer=lambda m: json.dumps(m).encode('utf-8')
  )
  producer.send(topic, value={"test_msg": "hello world"}).add_callback(on_send_success).add_callback(
    on_send_error)
  # close() 方法会阻塞等待之前所有的发送请求完成后再关闭 KafkaProducer
  producer.close()
if __name__ == '__main__':
  # main()
  main2()

消费者

kafka的消费模型比较复杂, 我会分以下几种情况来进行说明

1.不使用消费组(group_id=None)

不使用消费组的情况下可以启动很多个消费者, 不再受限于分区数, 即使消费者数量 > 分区数, 每个消费者也都可以收到消息

# -*- coding: utf-8 -*-

'''
消费者: group_id=None
'''
from kafka import KafkaConsumer
topic = 'demo'
def main():
  consumer = KafkaConsumer(
    topic,
    bootstrap_servers='localhost:9092',
    auto_offset_reset='latest',
    # auto_offset_reset='earliest',
  )
  for msg in consumer:
    print(msg)
    print(msg.value)
  consumer.close()
if __name__ == '__main__':
  main()

2.指定消费组

以下使用pool方法来拉取消息

pool 每次拉取只能拉取一个分区的消息, 比如有2个分区1个consumer, 那么会拉取2次

pool 是如果有消息马上进行拉取, 如果timeout_ms内没有新消息则返回空dict, 所以可能出现某次拉取了1条消息, 某次拉取了max_records条

# -*- coding: utf-8 -*-

'''
消费者: 指定group_id
'''

from kafka import KafkaConsumer

topic = 'demo'
group_id = 'test_id'


def main():
  consumer = KafkaConsumer(
    topic,
    bootstrap_servers='localhost:9092',
    auto_offset_reset='latest',
    group_id=group_id,

  )
  while True:
    try:
      # return a dict
      batch_msgs = consumer.poll(timeout_ms=1000, max_records=2)
      if not batch_msgs:
        continue
      '''
      {TopicPartition(topic='demo', partition=0): [ConsumerRecord(topic='demo', partition=0, offset=42, timestamp=1576425111411, timestamp_type=0, key=None, value=b'74', headers=[], checksum=None, serialized_key_size=-1, serialized_value_size=2, serialized_header_size=-1)]}
      '''
      for tp, msgs in batch_msgs.items():
        print('topic: {}, partition: {} receive length: '.format(tp.topic, tp.partition, len(msgs)))
        for msg in msgs:
          print(msg.value)
    except KeyboardInterrupt:
      break

  consumer.close()


if __name__ == '__main__':
  main()

关于消费组

我们根据配置参数分为以下几种情况

  • group_id=None
    • auto_offset_reset='latest': 每次启动都会从最新出开始消费, 重启后会丢失重启过程中的数据
    • auto_offset_reset='latest': 每次从最新的开始消费, 不会管哪些任务还没有消费
  • 指定group_id
    • 全新group_id
      • auto_offset_reset='latest': 只消费启动后的收到的数据, 重启后会从上次提交offset的地方开始消费
      • auto_offset_reset='earliest': 从最开始消费全量数据
    • 旧group_id(即kafka集群中还保留着该group_id的提交记录)
      • auto_offset_reset='latest': 从上次提交offset的地方开始消费
      • auto_offset_reset='earliest': 从上次提交offset的地方开始消费

性能测试

以下是在本地进行的测试, 如果要在线上使用kakfa, 建议提前进行性能测试

producer

# -*- coding: utf-8 -*-

'''
producer performance

environment:
  mac
  python3.7
  broker 1
  partition 2
'''

import json
import time
from kafka import KafkaProducer

topic = 'demo'
nums = 1000000


def main():
  producer = KafkaProducer(
    bootstrap_servers='localhost:9092',
    value_serializer=lambda m: json.dumps(m).encode('utf-8')
  )
  st = time.time()
  cnt = 0
  for _ in range(nums):
    producer.send(topic, value=_)
    cnt += 1
    if cnt % 10000 == 0:
      print(cnt)

  producer.flush()

  et = time.time()
  cost_time = et - st
  print('send nums: {}, cost time: {}, rate: {}/s'.format(nums, cost_time, nums // cost_time))


if __name__ == '__main__':
  main()

'''
send nums: 1000000, cost time: 61.89236712455749, rate: 16157.0/s
send nums: 1000000, cost time: 61.29534196853638, rate: 16314.0/s
'''

consumer

# -*- coding: utf-8 -*-

'''
consumer performance
'''

import time
from kafka import KafkaConsumer

topic = 'demo'
group_id = 'test_id'


def main1():
  nums = 0
  st = time.time()

  consumer = KafkaConsumer(
    topic,
    bootstrap_servers='localhost:9092',
    auto_offset_reset='latest',
    group_id=group_id
  )
  for msg in consumer:
    nums += 1
    if nums >= 500000:
      break
  consumer.close()

  et = time.time()
  cost_time = et - st
  print('one_by_one: consume nums: {}, cost time: {}, rate: {}/s'.format(nums, cost_time, nums // cost_time))


def main2():
  nums = 0
  st = time.time()

  consumer = KafkaConsumer(
    topic,
    bootstrap_servers='localhost:9092',
    auto_offset_reset='latest',
    group_id=group_id
  )
  running = True
  batch_pool_nums = 1
  while running:
    batch_msgs = consumer.poll(timeout_ms=1000, max_records=batch_pool_nums)
    if not batch_msgs:
      continue
    for tp, msgs in batch_msgs.items():
      nums += len(msgs)
      if nums >= 500000:
        running = False
        break

  consumer.close()

  et = time.time()
  cost_time = et - st
  print('batch_pool: max_records: {} consume nums: {}, cost time: {}, rate: {}/s'.format(batch_pool_nums, nums,
                                              cost_time,
                                              nums // cost_time))


if __name__ == '__main__':
  # main1()
  main2()

'''
one_by_one: consume nums: 500000, cost time: 8.018627166748047, rate: 62354.0/s
one_by_one: consume nums: 500000, cost time: 7.698841094970703, rate: 64944.0/s


batch_pool: max_records: 1 consume nums: 500000, cost time: 17.975456953048706, rate: 27815.0/s
batch_pool: max_records: 1 consume nums: 500000, cost time: 16.711708784103394, rate: 29919.0/s

batch_pool: max_records: 500 consume nums: 500369, cost time: 6.654940843582153, rate: 75187.0/s
batch_pool: max_records: 500 consume nums: 500183, cost time: 6.854053258895874, rate: 72976.0/s

batch_pool: max_records: 1000 consume nums: 500485, cost time: 6.504687070846558, rate: 76942.0/s
batch_pool: max_records: 1000 consume nums: 500775, cost time: 7.047331809997559, rate: 71058.0/s
'''

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python判断操作系统类型代码分享
Nov 22 Python
浅谈python类属性的访问、设置和删除方法
Jul 25 Python
python基于http下载视频或音频
Jun 20 Python
python经典趣味24点游戏程序设计
Jul 26 Python
pytorch 可视化feature map的示例代码
Aug 20 Python
Python生态圈图像格式转换问题(推荐)
Dec 02 Python
pytorch GAN生成对抗网络实例
Jan 10 Python
Python3.7.0 Shell添加清屏快捷键的实现示例
Mar 23 Python
matplotlib subplot绘制多个子图的方法示例
Jul 28 Python
PyTorch如何搭建一个简单的网络
Aug 24 Python
python代码实现猜拳小游戏
Nov 30 Python
python mongo 向数据中的数组类型新增数据操作
Dec 05 Python
Python面向对象之继承原理与用法案例分析
Dec 31 #Python
pytorch中nn.Conv1d的用法详解
Dec 31 #Python
Python实现剪刀石头布小游戏(与电脑对战)
Dec 31 #Python
Pytorch之卷积层的使用详解
Dec 31 #Python
Python中bisect的使用方法
Dec 31 #Python
pytorch中tensor张量数据类型的转化方式
Dec 31 #Python
Pytorch之parameters的使用
Dec 31 #Python
You might like
PHP文章采集URL补全函数(FormatUrl)
2012/08/02 PHP
PHP抓取、分析国内视频网站的视频信息工具类
2014/04/02 PHP
PHP Session机制简介及用法
2014/08/19 PHP
php实现当前页面点击下载文件的实例代码
2016/11/16 PHP
Yii框架操作cookie与session的方法实例详解
2019/09/04 PHP
jQuery创建自己的插件(自定义插件)的方法
2010/06/10 Javascript
理解Javascript_03_javascript全局观
2010/10/11 Javascript
actionscript与javascript的区别
2011/05/25 Javascript
基于jQuery.Validate验证库知识点的详解
2013/04/26 Javascript
客户端js性能优化小技巧整理
2013/11/05 Javascript
javascript 实现map集合
2015/04/03 Javascript
微信浏览器内置JavaScript对象WeixinJSBridge使用实例
2015/05/25 Javascript
Javascript简写条件语句(推荐)
2016/06/12 Javascript
JS 动态加载js文件和css文件 同步/异步的两种简单方式
2016/09/23 Javascript
微信小程序 数据绑定详解及实例
2016/10/25 Javascript
Ionic2系列之使用DeepLinker实现指定页面URL
2016/11/21 Javascript
jquery 追加元素append、prepend、before、after用法与区别分析
2016/12/02 Javascript
jQuery插件FusionWidgets实现的AngularGauge图效果示例【附demo源码】
2017/03/23 jQuery
element-ui多文件上传的实现示例
2019/04/10 Javascript
vue2.0实现列表数据增加和删除
2020/06/17 Javascript
详解python的webrtc库实现语音端点检测
2017/05/31 Python
python3.6使用urllib完成下载的实例
2018/12/19 Python
PyQt5图形界面播放音乐的实例
2019/06/17 Python
python代码编写计算器小程序
2020/03/30 Python
python+logging+yaml实现日志分割
2019/07/22 Python
python爬虫 批量下载zabbix文档代码实例
2019/08/21 Python
Flask框架请求钩子与request请求对象用法实例分析
2019/11/07 Python
利用PyQt中的QThread类实现多线程
2020/02/18 Python
基于pytorch padding=SAME的解决方式
2020/02/18 Python
解决Keras自带数据集与预训练model下载太慢问题
2020/06/12 Python
python实现学生成绩测评系统
2020/06/22 Python
CSS3 画基本图形,圆形、椭圆形、三角形等
2016/09/20 HTML / CSS
New delete 与malloc free 的联系与区别
2013/02/04 面试题
2014年防汛工作总结
2014/12/08 职场文书
解决mysql问题:由于找不到MSVCR120.dll,无法继续执行代码
2021/06/26 MySQL
python图像处理 PIL Image操作实例
2022/04/09 Python