深入了解如何基于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操作摄像头截图实现远程监控的例子
Mar 25 Python
python使用PIL缩放网络图片并保存的方法
Apr 24 Python
浅谈django中的认证与登录
Oct 31 Python
简单谈谈Python中的闭包
Nov 30 Python
Python 结巴分词实现关键词抽取分析
Oct 21 Python
Python数据结构与算法之图的广度优先与深度优先搜索算法示例
Dec 14 Python
python中协程实现TCP连接的实例分析
Oct 14 Python
Python实现把类当做字典来访问
Dec 16 Python
pandas-resample按时间聚合实例
Dec 27 Python
PyQt5中多线程模块QThread使用方法的实现
Jan 31 Python
完美解决jupyter由于无法import新包的问题
May 26 Python
python实现计算图形面积
Feb 22 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
在DC的漫画和电影中,蝙蝠侠的宿敌,小丑的真名是什么?
2020/04/09 欧美动漫
php数据类型判断函数有哪些
2013/09/23 PHP
laravel添加前台跳转成功页面示例
2019/10/22 PHP
采用CSS和JS,刚好我最近有个站点要用到下拉菜单!
2006/06/26 Javascript
js 表单验证方法(实用)
2009/04/28 Javascript
JavaScript 未结束的字符串常量常见解决方法
2010/01/24 Javascript
JS中setTimeout()的用法详解
2013/04/14 Javascript
js 与 php 通过json数据进行通讯示例
2014/03/26 Javascript
js控制鼠标事件移动及移出效果显示
2014/10/19 Javascript
JS模仿编辑器实时改变文本框宽度和高度大小的方法
2015/08/17 Javascript
js实现楼层效果的简单实例
2016/07/15 Javascript
基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)
2016/08/29 Javascript
Swiper 4.x 使用方法(移动端网站的内容触摸滑动)
2018/05/17 Javascript
Vue项目部署的实现(阿里云+Nginx代理+PM2)
2019/03/26 Javascript
小程序自定义弹框效果
2020/11/16 Javascript
Python实现的圆形绘制(画圆)示例
2018/01/31 Python
django反向解析URL和URL命名空间的方法
2018/06/05 Python
Python批量删除只保留最近几天table的代码实例
2019/04/01 Python
python实现猜拳小游戏
2020/04/05 Python
python科学计算之narray对象用法
2019/11/25 Python
python 字典套字典或列表的示例
2019/12/16 Python
Django 再谈一谈json序列化
2020/03/16 Python
Python实现UDP程序通信过程图解
2020/05/15 Python
SkinCeuticals官网:美国药妆品牌
2018/04/19 全球购物
西班牙电子产品购物网站:Electronicamente
2018/07/26 全球购物
自考毕业生自我鉴定
2013/11/04 职场文书
新郎新娘婚礼答谢词
2014/01/11 职场文书
五四青年节的活动方案
2014/08/20 职场文书
2014年设计师工作总结
2014/11/25 职场文书
如何解决.cuda()加载用时很长的问题
2021/05/24 Python
新手必备Python开发环境搭建教程
2021/05/28 Python
MySQL完整性约束的定义与实例教程
2021/05/30 MySQL
HTML+CSS 实现顶部导航栏菜单制作
2021/06/03 HTML / CSS
Python实现信息轰炸工具(再也不怕说不过别人了)
2021/06/11 Python
SQL Server查询某个字段在哪些表中存在
2022/03/03 SQL Server
关于mysql中string和number的转换问题
2022/06/14 MySQL