Python 实现Kafka SASL认证生产消费

Python 实现Kafka SASL认证生产消费,解决断线重连、消息异常问题
在实际的后端开发、数据流转场景中,Kafka 作为高吞吐、高可靠的消息中间件,被广泛用于业务解耦、数据异步传输。而线上 Kafka 集群基本都会开启 SASL 安全认证,避免未授权访问。本文基于 Python 实现带SASL账号密码认证的 Kafka 生产者、消费者完整逻辑,同时解决了日常开发中常见的断线掉线、单条消息异常阻塞、会话超时等问题,适配生产环境使用。

一、整体方案设计思路

本次封装的 Kafka 工具分为生产者和消费者两大模块,全程采用配置文件解耦核心参数,避免硬编码服务器地址、账号密码、超时时间等关键配置,提升代码可维护性和通用性。
核心设计原则:配置与代码分离、异常容错、服务稳定常驻、消息不阻塞。所有 Kafka 集群地址、认证方式、消费分组、超时参数等均统一写入 ini 配置文件,业务代码仅专注于消息发送和消息消费逻辑,便于后续集群变更、参数调优,无需修改业务代码。

二、Kafka 生产者核心实现与优势

1. SASL 安全认证初始化

生产者基于官方 kafka-python 库初始化,完整适配 SASL_PLAINTEXT 认证模式,配置文件读取账号、密码、加密机制、安全协议等参数,严格匹配线上 Kafka 集群认证规则,保障消息传输的安全性,杜绝匿名访问风险。

2. 同步可靠消息发送机制

采用同步发送模式生产消息,通过 future.get() 阻塞获取消息发送结果,精准捕获消息的 Topic、分区、偏移量信息,方便日志排查和问题追溯。相较于异步发送,同步模式更适合对消息可靠性要求较高的业务场景,确保每条消息都成功投递。
同时配置了消息重试机制,针对临时网络波动、集群短暂不可用等问题,自动重试发送,大幅提升消息投递成功率。并且自定义 JSON 序列化规则,支持中文正常传输,避免默认编码导致的中文乱码问题。

3. 异常容错处理

对 Kafka 专属异常进行单独捕获,发送失败时打印详细报错信息并返回失败标识,方便上层业务做重试、告警等拓展逻辑。测试场景下通过循环调用验证接口稳定性,搭配手动刷新缓冲区,确保消息即时推送至集群。

三、Kafka 消费者核心优化与亮点

1. 常驻式自动重连机制

原生 Kafka 消费者存在断连后无法自动恢复的问题,网络波动、集群重启都会导致消费服务终止。本次封装通过外层死循环+异常捕获休眠重试的方案,实现消费者断线自动重建,一旦检测到 Kafka 连接异常、未知运行异常,服务不会宕机,休眠5秒后自动重启消费任务,保障服务7×24小时常驻运行。

2. 规避会话超时掉线问题

优化原生消费者配置,删除多余的会话超时参数,依托 Kafka 原生长轮询机制,自动维持心跳会话,持续与 Broker 保持连接,有效避免常规配置不当导致的消费者被集群剔除、频繁掉线等问题,极大提升消费稳定性。

3. 单条消息异常隔离

消费循环中增加单条消息独立异常捕获逻辑,当某一条消息格式错误、字段缺失、业务处理异常时,不会中断整体消费流程,仅跳过当前异常消息并打印日志,保证后续正常消息可以持续消费,避免单点异常导致整个消费服务瘫痪。

4. 手动提交偏移量,精准可控

采用手动提交 offset 的方式,在单条消息完整处理完成后再提交偏移量。相较于自动提交,手动提交可以精准保证消息处理成功才标记消费完成,避免业务处理失败但偏移量已提交,导致消息丢失的问题,完美适配业务一致性要求较高的场景。

四、配置文件解耦设计

所有核心参数统一收纳在 config.ini 配置文件中,分为基础集群配置、生产者配置、消费者配置三大模块。涵盖集群地址、主题名称、认证信息、超时时间、消费分组、初始偏移量策略等全部参数。
这种设计极大提升了代码的通用性,开发、测试、生产环境可通过切换配置文件快速适配,后续集群扩容、认证信息更新、参数调优,无需改动业务代码,降低维护成本。

五、实战落地总结

这套 Kafka 生产消费方案,完全适配线上 SASL 认证集群,解决了 Python 原生 Kafka 封装常见的中文乱码、服务掉线、消息阻塞、消息丢失、配置混乱等痛点。整体代码轻量化、无冗余依赖,容错机制完善、稳定性高,可直接复用至日常数据同步、业务消息推送、日志采集等各类业务场景。
同时代码结构清晰、拓展性强,可基于现有逻辑快速新增消息重试、异常消息落盘、消费监控告警、批量消费等功能,适配复杂的生产环境需求。
依赖包

复制代码
pip install kafka-python==2.3.0

配置文件 config.ini

复制代码
 

[KAFKA_BASE]
bootstrap_servers = 192.168.0.10:9000
topic = JavaToPython

[KAFKA_PRODUCER]
security_protocol = SASL_PLAINTEXT
sasl_mechanism = PLAIN
sasl_plain_username = admin
sasl_plain_password = admin-secret
max_request_size = 10485760
send_timeout = 3000

[KAFKA_CONSUMER]
group_id = JavaToPythonGroup
security_protocol = SASL_PLAINTEXT
sasl_mechanism = PLAIN
sasl_plain_username = admin
sasl_plain_password = admin-secret
auto_offset_reset = latest
enable_auto_commit = False
#auto_commit_interval_ms = 1000
#consumer_timeout_ms = 5000

生产者 kafka_producer.py

复制代码
import configparser
import json
from kafka import KafkaProducer
from kafka.errors import KafkaError
import os
 

# 获取当前脚本文件所在文件夹
script_dir = os.path.dirname(os.path.abspath(__file__))
# 拼接config.ini完整路径
ini_path = os.path.join(script_dir, "config.ini")

# 加载ini配置
conf = configparser.ConfigParser()
# read返回成功读取的文件列表,可打印校验
load_files = conf.read(ini_path, encoding="utf-8")
print("成功加载的配置文件:", load_files)
 
base_conf = conf["KAFKA_BASE"]
prod_conf = conf["KAFKA_PRODUCER"]

# 初始化带SASL认证生产者
producer = KafkaProducer(
    bootstrap_servers=base_conf["bootstrap_servers"].split(","),
    security_protocol=prod_conf["security_protocol"],
    sasl_mechanism=prod_conf["sasl_mechanism"],
    sasl_plain_username=prod_conf["sasl_plain_username"],
    sasl_plain_password=prod_conf["sasl_plain_password"],
    max_request_size=int(prod_conf["max_request_size"]),
    retries=3,
    # json序列化
    value_serializer=lambda v: json.dumps(v, ensure_ascii=False).encode("utf-8")
)


def send_sync_msg(msg_data: dict):
    """同步发送json业务消息"""
    try:
        future = producer.send(topic=base_conf["topic"], value=msg_data)
        meta = future.get(timeout=int(prod_conf["send_timeout"]) / 1000)
        print(f"发送成功 | topic:{meta.topic} 分区:{meta.partition} offset:{meta.offset}")
        return True
    except KafkaError as e:
        print(f"发送失败:{str(e)}")
        return False



if __name__ == "__main__":
    
  
    test_msg = {
        "resourceId": 184501,
        "type": "文档",
        "resourceType": "txt"
    }

 
    for i in range(2):
        print(f"===== 第{i+1}次调用 =====")
        send_sync_msg(test_msg)
        producer.flush()

 

消费者 kafka_consumer.py

复制代码
import configparser
import json
import time
from kafka import KafkaConsumer
from kafka.errors import KafkaError
import os

# 读取ini配置
# 获取当前脚本文件所在文件夹
script_dir = os.path.dirname(os.path.abspath(__file__))
# 拼接config.ini完整路径
ini_path = os.path.join(script_dir, "config.ini")

# 加载ini配置
conf = configparser.ConfigParser()
# read返回成功读取的文件列表,可打印校验
load_files = conf.read(ini_path, encoding="utf-8")
print("成功加载的配置文件:", load_files)
base_conf = conf["KAFKA_BASE"]
cons_conf = conf["KAFKA_CONSUMER"]

def create_consumer():
    """封装创建消费者实例"""
    consumer = KafkaConsumer(
        base_conf["topic"],
        bootstrap_servers=base_conf["bootstrap_servers"].split(","),
        group_id=cons_conf["group_id"],
        security_protocol=cons_conf["security_protocol"],
        sasl_mechanism=cons_conf["sasl_mechanism"],
        sasl_plain_username=cons_conf["sasl_plain_username"],
        sasl_plain_password=cons_conf["sasl_plain_password"],
        auto_offset_reset=cons_conf["auto_offset_reset"],
        enable_auto_commit=conf.getboolean("KAFKA_CONSUMER", "enable_auto_commit"),
        # auto_commit_interval_ms=int(cons_conf["auto_commit_interval_ms"]),
        # 删掉 consumer_timeout_ms,避免会话超时掉线
        value_deserializer=lambda x: json.loads(x.decode("utf-8"))
    )
    return consumer

def consume_loop():
    print(f"消费任务启动,group:{cons_conf['group_id']} topic:{base_conf['topic']}")
    # 外层死循环:断连后自动重建消费者
    while True:
        try:
            consumer = create_consumer()
            print("Kafka消费者连接成功,持续监听新消息...")
            # 原生长轮询,自动发送心跳维持会话,不会被broker剔除
            for msg in consumer:
                try:
                    data = msg.value
                    print("=" * 60)
                    print(f"topic: {msg.topic}")
                    print(f"partition: {msg.partition}")
                    print(f"offset: {msg.offset}")
                    print(f"headers: {msg.headers}")
                    print(f"消息内容: {data}")

                    # 逐个取出每个字段
                    resource_id = data["resourceId"]
                   

                    # 打印使用
                    print("resourceId =", resource_id)
                 
                    # 此处写你的业务处理逻辑  
                    consumer.commit()
                except Exception as single_err:
                    print(f"单条消息处理异常,跳过本条:{single_err}")
                    continue
        except KafkaError as e:
            print(f"Kafka连接异常,5秒后重连:{str(e)}")
        except Exception as e:
            print(f"消费循环未知异常,5秒后重启:{str(e)}")
        # 异常后休眠5秒再重试
        time.sleep(5)

if __name__ == "__main__":
    consume_loop()
相关推荐
m0_617493943 小时前
Python OpenCV 透视变换(Perspective Transform)详解与实战
开发语言·python·opencv
小李不困还能学3 小时前
PyCharm下载安装与配置教程
ide·python·pycharm
博观而约取厚积而薄发3 小时前
Pytest 从入门到精通,一篇就够(超详细实战教程)
python·测试工具·单元测试·自动化·pytest
imzed3 小时前
使用 Playwright + Pytest 构建 Web UI 自动化测试框架
python·自动化·pytest
普通网友3 小时前
pytest一些常见的插件
开发语言·python·pytest
时空系3 小时前
Python 高性能高压缩打包器 —— 基于 JianPy 语义分析引擎
python
cndes4 小时前
给Miniconda换源,让包下载更迅速
开发语言·python
Metaphor6924 小时前
使用 Python 添加、隐藏和删除 PDF 图层
python·pdf·图层