python kafka获取对应时间范围内的消息实例代码

本文详细介绍了如何在Kafka中使用offsetsForTimes函数,确保数据的时间戳与偏移量正确对应。需注意Kafka版本至少为0.11,且生产者配置与log.message.timestamp.type参数开启。文章通过示例代码展示了如何设置消费者,查找特定时间窗口内的消息偏移量。

应用offsets_for_times前提是kafka版本0.11及以上版本

并且跟producer配置也有关系

使用KafkaConsumer.offsetsForTimes要确认集群已开启log.message.timestamp.type参数,并且clien要使用0.10.*以及以上的客户端发送数据,数据格式和0.9不同了

具体可看官方文档,在kafka节点上会除了.index和.log文件还多了一个时间跟offset对应关系的文件。

flow_test.py

# coding: utf-8
import sys
import datetime
import time

reload(sys)
sys.setdefaultencoding('utf-8')
import json
import hashlib
import ConfigParser
import OaDataSystem
import sys

sys.setdefaultencoding('utf-8')
import CrmOperator
from kafka import KafkaConsumer
from kafka.structs import TopicPartition, BrokerMetadata
from kafka.errors import KafkaError
import json
import send_email
from threading import Thread
from time import ctime, sleep
import redis
import traceback


class kafkaprocess():

    def msg_process(self, message_set, class_set):
        class_set['kafka_log'].logOperator(1, str(message_set))
        for msg in message_set:
            myjson = json.loads(msg.value.decode('utf-8'))
            if int(myjson['data']['teacher_id']) == 6557:
                print "test0=====",msg.key.decode('utf-8')

def getmd5(filename):
    fd = open(filename, "r")
    fcont = fd.read()
    fd.close()
    fmd5 = hashlib.md5(fcont)
    return fmd5.hexdigest()


def working(config):
    global conf_md5
    while True:
        sleep(5)
        conf_ing_md5 = getmd5('para.conf')
        if conf_md5 != conf_ing_md5:
            config.read("para.conf")
            conf_md5 = conf_ing_md5

def get_offset_time_window(consumer, partitions_structs, begin_time, end_time):
    begin_search = {}
    for partition in partitions_structs:
        begin_search[partition] = begin_time if isinstance(begin_time, int) else __str_to_timestamp(begin_time)
    begin_offset = consumer.offsets_for_times(begin_search)
    print "test31=====",begin_search
    print "test32=======",begin_offset
    end_search = {}
    for partition in partitions_structs:
        end_search[partition] = end_time if isinstance(end_time, int) else __str_to_timestamp(end_time)
    end_offset = consumer.offsets_for_times(end_search)

    for topic_partition, offset_and_timestamp in begin_offset.items():
        b_offset = 'null' if offset_and_timestamp is None else offset_and_timestamp[0]
        e_offset = 'null' if end_offset[topic_partition] is None else end_offset[topic_partition][0]
        print('Between {0} and {1}, {2} offset range = [{3}, {4}]'.format(begin_time, end_time, topic_partition, b_offset, e_offset))
        if b_offset != 'null':
            print "test33========",topic_partition,b_offset
            consumer.seek(topic_partition,b_offset)
    return consumer,e_offset

def __str_to_timestamp(str_time, format_type='%Y-%m-%d %H:%M:%S'):
    time_array = time.strptime(str_time, format_type)
    return int(time.mktime(time_array)) * 1000

def workline1():
    try:
        begin_time = '2019-08-01 10:54:15'
        end_time = '2019-08-20 19:01:15'
       # consumer = KafkaConsumer(group_id=config.get("db", "main_group_id"),
       #                          bootstrap_servers=config.get("db", "bootstrap_servers"))
        consumer = KafkaConsumer(group_id=config.get("db", "main_group_id"),
                                 sasl_plain_username='xes_oa', sasl_plain_password='CnYN88zKd44tV7ng',
                                 security_protocol='SASL_PLAINTEXT', sasl_mechanism='PLAIN',
                                 bootstrap_servers=config.get("db", "bootstrap_servers")
                                 )

        tps = []
        for p in consumer.partitions_for_topic(str(config.get("db", "main_topic_id"))):
            tp = TopicPartition(str(config.get("db", "main_topic_id")), p)
            tps.append(tp)
        print "test30======",consumer.config['api_version'],tps
        consumer.assign(tps)
        consumer, end_offset = get_offset_time_window(consumer, tps, begin_time, end_time)
        
        message_sets = []
        start_time = end_time = int(time.time())
        while True:
            try:
                value_ans = consumer.poll(max_records=20).values()
                if len(value_ans) > 0:
                    for par in value_ans:
                        msg_offset = int(par.offset)
                        msg_partition = int(par.partition)
                        msg_topic = str(par.topic)
                        if (对应分片的截止时间戳的end_offset到达):
                            停止
                        message_sets += par
                    kp.msg_process(message_sets, class_set)
                    del message_sets[:]
            except Exception, e:
                traceback.print_exc()
                print Exception, ":================", e 
    except Exception, e:
        print Exception, ":", e


conf_md5 = getmd5('para.conf')
# read the config file
config = ConfigParser.ConfigParser()
config.read("para.conf")
print 'config...'

if __name__ == '__main__':
    print time.time()

    class_set = {}
    class_set['kafka_log'] = CrmOperator.CrmOperator('kafka_flow_test', 'all-type')

    kp = kafkaprocess()
    # config check work thread
    t1 = Thread(target=working, args=(config,))
    t1.setDaemon(True)
    t1.start()

    # the main binglog thread
    lineT1 = Thread(target=workline1)
    lineT1.setDaemon(True)
    lineT1.start()

    print 'main thread running'

    while True:
        sleep(5)


para.conf


[table_keys]
#table the index key ,maybe are multiple keys
xes_student_live_op_logs=id

[db]
bootstrap_servers=nodelist

main_group_id=flow_test_englive_2019082001
#if set this  param ,when restart the program , group_id is oa_maxwell_20170209 and the offset is the lastest offset for main_topic_id  subtraction the main_restart_offset (lastest -300)
main_restart_offset=0
main_topic_id=xxxx

 

<think>我们正在处理的是Kafka消息队列,用户希望根据时间点定位消息的位置(偏移量)。在Kafka中,我们可以通过时间戳来查询消息的偏移量。根据之前的讨论,我们知道Kafka消息包含时间戳(timestamp),并且Kafka提供了按时间戳查询偏移量的API。具体来说,我们可以使用`offsets_for_times`方法来获取指定时间戳对应的偏移量。步骤:1.创建KafkaConsumer实例。2.指定主题和分区。3.使用`offsets_for_times`方法,传入一个字典,键为TopicPartition,值为要查询的时间戳(毫秒)。4.该方法返回一个字典,其中包含每个TopicPartition对应的偏移量和时间戳信息。注意:时间戳可以是以下两种之一:-消息时间戳(生产者发送消息时设置的时间戳)-日志追加时间(broker配置为使用日志追加时间)另外,如果查询的时间戳早于该分区最早消息时间戳,则返回最早消息的偏移量;如果晚于最新消息时间戳,则返回最新消息的偏移量(注意:此时返回的偏移量可能指向一条尚未存在的消息,因此在使用时需要判断)。我们还可以结合使用`beginning_offsets`和`end_offsets`来获取分区的起始和结束偏移量,以判断查询结果是否在有效范围内。下面给出具体代码实现:</think>### 如何根据时间点定位消息位置(日志分析/消息队列/系统调试) #### 1. Kafka 时间戳查询偏移量 在Kafka中,可以通过时间戳精确查找消息位置: ```python from kafka import KafkaConsumer, TopicPartition from datetime import datetime, timezone def find_offset_by_time(bootstrap_servers, topic, partition, timestamp): """根据时间戳定位消息偏移量""" consumer = KafkaConsumer( bootstrap_servers=bootstrap_servers, enable_auto_commit=False ) tp = TopicPartition(topic, partition) consumer.assign([tp]) # 将时间转换为毫秒时间戳 target_ts = int(timestamp.timestamp() * 1000) # 核心API:按时间戳查询偏移量 offsets = consumer.offsets_for_times({tp: target_ts}) if offsets and offsets[tp]: return { "offset": offsets[tp].offset, "timestamp": offsets[tp].timestamp, "partition": partition } return None # 使用示例 target_time = datetime(2023, 10, 15, 14, 30, tzinfo=timezone.utc) position = find_offset_by_time( bootstrap_servers='kafka:9092', topic='transactions', partition=0, timestamp=target_time ) print(f"消息位置: {position}") ``` #### 2. 日志文件时间戳定位 对于系统日志文件(如Nginx/Apache日志),使用时间戳过滤: ```bash # 使用grep定位特定时间范围的日志 grep "15/Oct/2023:14:30" /var/log/nginx/access.log # 使用journalctl查询系统日志 journalctl --since "2023-10-15 14:30:00" --until "2023-10-15 14:35:00" ``` #### 3. 消息队列时间戳检索 不同消息队列的时间戳查询方法: | 消息队列 | 时间查询API | 示例 | |---------|------------|------| | **RabbitMQ** | `basic.get` + 消息属性 | `message.properties.timestamp` | | **RocketMQ** | `searchOffsetByTimestamp` | `consumer.searchOffset(topic, partition, timestamp)` | | **ActiveMQ** | 消息选择器 | `session.createConsumer(destination, "JMSTimestamp > 1697373000000")` | #### 4. 日志分析系统定位 在ELK/Splunk等系统中通过时间范围查询: ```json // Kibana DSL查询示例 { "query": { "range": { "@timestamp": { "gte": "2023-10-15T14:30:00", "lte": "2023-10-15T14:35:00" } } } } ``` #### 关键原理 1. **时间戳类型**: - **创建时间**:生产者生成消息时间[^4] - **追加时间**:消息写入broker的时间[^1] - **处理时间**:消费者处理消息时间[^3] 2. **时间精度处理**: $$ \text{时间戳} = \frac{\text{UNIX时间(秒)} \times 1000 + \text{毫秒}}{1} $$ ```python # 时区转换示例 utc_time = datetime.now(timezone.utc) local_time = utc_time.astimezone() ``` #### 调试场景应用 1. **消息丢失排查**: ```python # 对比生产/消费时间差 producer_time = msg.timestamp consumer_time = datetime.now().timestamp() * 1000 latency = consumer_time - producer_time ``` 2. **死信队列分析**: ```python # 检查死信队列中的超时消息 dlq_msg = dead_letter_queue.get_message() if dlq_msg.timestamp < (time.time() - 3600)*1000: print("发现超过1小时的死信消息") ``` 3. **日志时间同步**: ```bash # 使用NTP同步服务器时间 sudo ntpdate pool.ntp.org ``` > 提示:对于关键业务系统,建议在消息体中嵌入唯一ID和时间戳,实现双重定位机制[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值