深入了解如何基于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基于pygame实现的弹力球效果(附源码)
Nov 11 Python
Python的Django框架中消息通知的计数器实现教程
Jun 13 Python
Python如何判断数独是否合法
Sep 08 Python
Python实现生成随机数据插入mysql数据库的方法
Dec 25 Python
python email smtplib模块发送邮件代码实例
Apr 26 Python
python的concat等多种用法详解
Nov 28 Python
Python实现爬取马云的微博功能示例
Feb 16 Python
浅谈python之高阶函数和匿名函数
Mar 21 Python
python实现AES加密和解密
Mar 27 Python
django drf框架中的user验证以及JWT拓展的介绍
Aug 12 Python
通过python扫描二维码/条形码并打印数据
Nov 14 Python
使用matplotlib的pyplot模块绘图的实现示例
Jul 12 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+Ajax实现表单验证的详解
2013/06/25 PHP
php foreach循环中使用引用的问题
2013/11/06 PHP
PHP树的深度编历生成迷宫及A*自动寻路算法实例分析
2015/03/10 PHP
CodeIgniter配置之database.php用法实例分析
2016/01/20 PHP
PHP获取页面执行时间的方法(推荐)
2016/12/10 PHP
PHP多维数组指定多字段排序的示例代码
2018/05/16 PHP
PHP+redis实现的限制抢购防止商品超发功能详解
2019/09/19 PHP
JS 控制非法字符的输入代码
2009/12/04 Javascript
下载文件个别浏览器文件名乱码解决办法
2013/03/19 Javascript
jQuery性能优化的38个建议
2014/03/04 Javascript
仅9张思维导图帮你轻松学习Javascript 就这么简单
2016/06/01 Javascript
jQuery实现iframe父窗体和子窗体的相互调用
2016/06/17 Javascript
关于Vue.js一些问题和思考学习笔记(1)
2016/12/02 Javascript
JS将unicode码转中文方法
2017/05/08 Javascript
Mac系统下Webstorm快捷键整理大全
2017/05/28 Javascript
详解vue mint-ui源码解析之loadmore组件
2017/10/11 Javascript
小程序和web画三角形实现解析
2019/09/02 Javascript
javascript跳转与返回和刷新页面的实例代码
2019/11/20 Javascript
vue 自定指令生成uuid滚动监听达到tab表格吸顶效果的代码
2020/09/16 Javascript
python学习必备知识汇总
2017/09/08 Python
python生成九宫格图片
2018/11/19 Python
使用python爬取抖音视频列表信息
2019/07/15 Python
python shell命令行中import多层目录下的模块操作
2020/03/09 Python
Python greenlet和gevent使用代码示例解析
2020/04/01 Python
新秀丽拉杆箱美国官方网站:Samsonite美国
2016/07/25 全球购物
土耳其家居建材网站:Koçtaş
2016/11/22 全球购物
Ralph Lauren拉夫·劳伦美国官网:带有浓郁美国气息的高品味时装品牌
2017/11/01 全球购物
中国制造网:Made-in-China.com
2019/10/25 全球购物
欧姆龙医疗欧洲有限公司:Omron Healthcare Europe B.V
2020/06/13 全球购物
自动化专业本科毕业生求职信
2013/10/20 职场文书
市场营销专业应届生自荐信
2014/06/19 职场文书
企业安全生产责任书范本
2014/07/28 职场文书
光学与应用专业毕业生求职信
2014/09/01 职场文书
2014年内部审计工作总结
2014/12/09 职场文书
25句企业管理语录:助你迅速打开思路,句句经典!
2020/01/14 职场文书
JS前端可扩展的低代码UI框架Sunmao使用详解
2022/07/23 Javascript