kafka消费堆积问题探索

背景

我们的商城项目用PHP写的,原本写日志方案用的是PHP的方案,但是,这个方案导致资源消耗一直降不下来,使用了20个CPU。后面考虑使用通过kafka的方案写日志,商城中把产生的日志丢到kafka中,在以go写的项目中消费kafka中的日志,并打印到控制台,最后,统一使用阿里sls抓取日志。我们kafka的分区有12个,go程序部署在k8s集群中,开启了弹性扩缩容,最多开启了8个pod进行消费,每秒产生的日志数量高峰在1500条左右,在这种情况下,依然产生了消息的堆积。

消费中执行的逻辑只有对象的映射和日志写控制台,所以,这种情况下产生了消息堆积,令我倍感困惑。

探索之路

第一步,确认一下每一步的执行时间。

func (s KafkaLogService) ReaderCreateLog(ctx context.Context, msg *customerkafka.CustomKafkaMsg) error {now := time.Now().UnixNano()var logEntry LogEntrydata, ok := msg.Data.(string)if !ok {global.GIN_LOG.Error(ctx, "消息数据类型错误", "data", msg.Data)return fmt.Errorf("消息数据类型错误: %v", msg.Data)}// 解析 JSON 数据if err := json.Unmarshal([]byte(data), &logEntry); err != nil {global.GIN_LOG.Error(ctx, "解析 JSON 数据失败:", "error", err, "message", data)return fmt.Errorf("解析消息失败: %w", err)}now2 := time.Now().UnixNano()fmt.Printf("*******************分隔符*******************Unmarshal logEntry 耗时:%d 纳秒\n", now2-now)var logMessage LogMessageif err := json.Unmarshal([]byte(logEntry.Message), &logMessage); err != nil {global.GIN_LOG.Error(ctx, "解析 JSON 数据失败:", "error", err, "message", logEntry.Message)return fmt.Errorf("解析消息失败: %w", err)}now3 := time.Now().UnixNano()fmt.Printf("*******************分隔符*******************Unmarshal LogMessage 耗时:%d 纳秒\n", now3-now2)// 日志等级判断switch logEntry.Level {case "ERROR":global.GIN_LOG.Error(ctx, "", zap.Any("data", logMessage), zap.String("project", logMessage.Project))case "WARN":global.GIN_LOG.Warn(ctx, "", zap.Any("data", logMessage), zap.String("project", logMessage.Project))case "INFO":global.GIN_LOG.Info(ctx, "", zap.Any("data", logMessage), zap.String("project", logMessage.Project))default:global.GIN_LOG.Warn(ctx, "", zap.Any("data", logMessage), zap.String("project", logMessage.Project))}now4 := time.Now().UnixNano()fmt.Printf("*******************分隔符*******************log 耗时:%d 纳秒\n", now4-now3)return nil
}

 

 json的Unmarshal的耗时,倒是符合我的认知,在预期之中。

但是,打印日志尽然需要耗时1.5毫秒,这个有点超出我的意料之外。这个时间似乎有点夸张啊。

但是,即便如此,一个消费者每秒也可以消费670条左右的消息,在起了8个实例的情况下,也不应该造成kafka消息的阻塞。

继续我们的探索之路

下面这一段是我对于kakfa消费者的封装。大概的逻辑就是每一个ActionType起一个协程进行消费。在这篇《基于kafka-go写的生产者和消费者》文章中写过这个封装背后的设计逻辑,有兴趣的可以移步过去一探究竟。

// Start 方法启动消费者并开始读取消息,根据actionType调用不同的处理函数
func (c *ConsumerClient) Start(ctx context.Context, handlers map[string]ActionHandler) error {for {select {case <-ctx.Done():return nil // 上下文取消,直接返回default:msg, err := c.reader.ReadMessage(ctx)if err != nil {c.logger.Error(ctx, "Failed to read message from Kafka", "error", err)continue}c.logger.Info(ctx, fmt.Sprintf("Message on topic: %s value: %s partion:%d offset:%d", msg.Topic, string(msg.Value), msg.Partition, msg.Offset))var kafkaMsg CustomKafkaMsgif err := json.Unmarshal(msg.Value, &kafkaMsg); err != nil {c.logger.Error(ctx, "Failed to unmarshal Kafka message", "error", err)continue}channel := make(chan *CustomKafkaMsg)// 使用 sync.Map 来管理 workerworker, loaded := c.workerMap.LoadOrStore(kafkaMsg.ActionType, channel)if !loaded {c.wg.Add(1) // 增加 WaitGroup 计数if ch, ok := worker.(chan *CustomKafkaMsg); ok {go c.startWorker(ctx, kafkaMsg.ActionType, handlers, ch)}}// 发送消息到对应的通道,避免阻塞其他消息消费// 只有在 handlers 中存在对应的 actionType 时才发送消息到对应的通道if _, ok := handlers[kafkaMsg.ActionType]; ok {if ch, ok := worker.(chan *CustomKafkaMsg); ok {ch <- &kafkaMsg}}}}
}

 由于我的这个写法,让我产生了一点担忧,虽然,我想的是每个ActionType只起一个协程进行消费,难道,实际情况并不是如我预期一样运行,而是,一条kafka消息就起了一个协程进行消费,如果是这种情况的话,那么,会导致大量的垃圾回收,程序的性能就会下降,那么,消息阻塞的问题也就可以解释了。

为了,验证我的这一想法,基于pprof工具看一下实际情况。

实际验证,排除我的担忧,符合我的预期,不是有一条kafka消息就开一个协程进行消费,而是,一个ActionType就只有一个协程进行消费。 

模拟生产环境测试

上述的探索,依然不能够完美解释文章开头提到的现象,起了8个消费者,依然导致消息堆积的现象。为了进一步探究其背后的原因,我模拟生产环境的状态,每秒钟往kafka中丢了1000条消息,再观察,我发现,在这种情况下,有时json.Unmarshal也有比较长的耗时,会出现1.5毫秒的耗时,另外,而写日志需要5毫秒左右,如此,每秒只能消费140条消息,消息堆积的现象也就能够解释了。

结论 

消息堆积的主要原因是日志打印操作耗时较长,最差时每秒只能消费140条消息。此外,有时JSON解析的时间也较长,这也是一个需要关注的问题。

接下来的目标是找出JSON解析耗时较长和日志打印慢的具体原因,并进行优化。通过解决这些问题,我们有望提高日志处理的效率,从而解决消息堆积的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/67207.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【opencv】第7章 图像变换

7.1 基 于OpenCV 的 边 缘 检 测 本节中&#xff0c;我们将一起学习OpenCV 中边缘检测的各种算子和滤波器——Canny 算子、Sobel 算 子 、Laplacian 算子以及Scharr 滤波器。 7.1.1 边缘检测的一般步骤 在具体介绍之前&#xff0c;先来一起看看边缘检测的一般步骤。 1.【第…

[Qt]常用控件介绍-多元素控件-QListWidget、QTableWidget、QQTreeWidget

目录 1.多元素控件介绍 2.ListWidget控件 属性 核心方法 核心信号 细节 Demo&#xff1a;编辑日程 3.TableWidget控件 核心方法 QTableWidgetItem核心信号 QTableWidgetItem核心方法 细节 Demo&#xff1a;编辑学生信息 4.TreeWidget控件 核心方法 核心信号…

[Linux]从零开始的STM32MP157交叉编译环境配置

一、前言 最近该忙的事情也是都忙完了&#xff0c;也是可以开始好好的学习一下Linux了。之前九月份的时候就想入手一块Linux的开发板用来学习Linux底层开发。之前在NXP和STM32MP系列之间犹豫&#xff0c;思来想去还是入手了一块STM32MP157。当然不是单纯因为MP157的性能在NXP之…

小程序如何引入腾讯位置服务

小程序如何引入腾讯位置服务 1.添加服务 登录 微信公众平台 注意&#xff1a;小程序要企业版的 第三方服务 -> 服务 -> 开发者资源 -> 开通腾讯位置服务 在设置 -> 第三方设置 中可以看到开通的服务&#xff0c;如果没有就在插件管理中添加插件 2.腾讯位置服务…

添加计算机到AD域中

添加计算机到AD域中 一、确定计算机的DNS指向域中的DNS二、打开系统设置三、加域成功后 一、确定计算机的DNS指向域中的DNS 二、打开系统设置 输入域管理员的账密 三、加域成功后 这里有显示&#xff0c;就成功了。

从epoll事件的视角探讨TCP:三次握手、四次挥手、应用层与传输层之间的联系

目录 一、应用层与TCP之间的联系 二、 当通信双方中的一方如客户端主动断开连接时&#xff0c;仅是在客户端的视角下连接已经断开&#xff0c;在服务端的眼中&#xff0c;连接依然存在&#xff0c;为什么&#xff1f;——触发EPOLLRDHUP事件&#xff1a;对端关闭连接或停止写…

使用RSyslog将Nginx Access Log写入Kafka

个人博客地址&#xff1a;使用RSyslog将Nginx Access Log写入Kafka | 一张假钞的真实世界 环境说明 CentOS Linux release 7.3.1611kafka_2.12-0.10.2.2nginx/1.12.2rsyslog-8.24.0-34.el7.x86_64.rpm 创建测试Topic $ ./kafka-topics.sh --zookeeper 192.168.72.25:2181/k…

使用 Docker 部署 Java 项目(通俗易懂)

目录 1、下载与配置 Docker 1.1 docker下载&#xff08;这里使用的是Ubuntu&#xff0c;Centos命令可能有不同&#xff09; 1.2 配置 Docker 代理对象 2、打包当前 Java 项目 3、进行编写 DockerFile&#xff0c;并将对应文件传输到 Linux 中 3.1 编写 dockerfile 文件 …

《研发管理 APQP 软件系统》——汽车电子行业的应用收益分析

全星研发管理 APQP 软件系统在汽车电子行业的应用收益分析 在汽车电子行业&#xff0c;技术革新迅猛&#xff0c;市场竞争激烈。《全星研发管理 APQP 软件系统》的应用&#xff0c;为企业带来了革命性的变化&#xff0c;诸多收益使其成为行业发展的关键驱动力。 《全星研发管理…

22、PyTorch nn.Conv2d卷积网络使用教程

文章目录 1. 卷积2. python 代码3. notes 1. 卷积 输入A张量为&#xff1a; A [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ] \begin{equation} A\begin{bmatrix} 0&1&2&3\\\\ 4&5&6&7\\\\ 8&9&10&11\\\\ 12&13&14&15 \end{b…

ASP.NET Core - 依赖注入(四)

ASP.NET Core - 依赖注入&#xff08;四&#xff09; 4. ASP.NET Core默认服务5. 依赖注入配置变形 4. ASP.NET Core默认服务 之前讲了中间件&#xff0c;实际上一个中间件要正常进行工作&#xff0c;通常需要许多的服务配合进行&#xff0c;而中间件中的服务自然也是通过 Ioc…

UE5游戏性能优化指南

解除帧率限制 启动游戏 按 “~” 键 输入 t.MaxFPS 200 可以解除默认帧率限制达到更高的帧率 UE游戏性能和场景优化思路&#xff1a; 1. 可以把可延展性调低&#xff0c;帧率会大幅提高&#xff0c;但画质会大幅降低 2.调整固定灯光&#xff0c;静态光源&#xff…

深度学习中的卷积和反卷积(四)——卷积和反卷积的梯度

本系列已完结&#xff0c;全部文章地址为&#xff1a; 深度学习中的卷积和反卷积&#xff08;一&#xff09;——卷积的介绍 深度学习中的卷积和反卷积&#xff08;二&#xff09;——反卷积的介绍 深度学习中的卷积和反卷积&#xff08;三&#xff09;——卷积和反卷积的计算 …

【C语言】线程

目录 1. 什么是线程 1.1概念 1.2 进程和线程的区别 1.3 线程资源 2. 函数接口 2.1创建线程: pthread_create 2.2 退出线程: pthread_exit 2.3 回收线程资源 练习 1. 什么是线程 1.1概念 线程是一个轻量级的进程&#xff0c;为了提高系统的性能引入线程。 在同一个进…

【C语言】字符串函数详解

文章目录 Ⅰ. strcpy -- 字符串拷贝1、函数介绍2、模拟实现 Ⅱ. strcat -- 字符串追加1、函数介绍2、模拟实现 Ⅲ. strcmp -- 字符串比较1、函数介绍2、模拟实现 Ⅳ. strncpy、strncat、strncmp -- 可限制操作长度Ⅴ. strlen -- 求字符串长度1、函数介绍2、模拟实现&#xff08…

Windows部署NVM并下载多版本Node.js的方法(含删除原有Node的方法)

本文介绍在Windows电脑中&#xff0c;下载、部署NVM&#xff08;node.js version management&#xff09;环境&#xff0c;并基于其安装不同版本的Node.js的方法。 在之前的文章Windows系统下载、部署Node.js与npm环境的方法&#xff08;https://blog.csdn.net/zhebushibiaoshi…

centos 8 中安装Docker

注&#xff1a;本次样式安装使用的是centos8 操作系统。 1、镜像下载 具体的镜像下载地址各位可以去官网下载&#xff0c;选择适合你们的下载即可&#xff01; 1、CentOS官方下载地址&#xff1a;https://vault.centos.org/ 2、阿里云开源镜像站下载&#xff1a;centos安装包…

STM32-笔记40-BKP(备份寄存器)

一、什么是BKP&#xff08;备份寄存器&#xff09;&#xff1f; 备份寄存器是42个16位的寄存器&#xff0c;可用来存储84个字节的用户应用程序数据。他们处在备份域里&#xff0c;当VDD电源被切断&#xff0c;他们仍然由VBAT维持供电。当系统在待机模式下被唤醒&#xff0c;或…

vue-cli项目配置使用unocss

在了解使用了Unocss后&#xff0c;就完全被它迷住了。接手过的所有项目都配置使用了它&#xff0c;包括一些旧项目&#xff0c;也跟同事分享了使用Unocss的便捷性。 这里分享一下旧项目如何配置和使用Unocss的&#xff0c;项目是vue2vue-cli构建的&#xff0c;node<20平常开…

新增文章分类功能

总说 过程参考黑马程序员SpringBoot3Vue3全套视频教程&#xff0c;springbootvue企业级全栈开发从基础、实战到面试一套通关_哔哩哔哩_bilibili 目录 总说 一、功能实现 1.1 Controller层 1.2 Service层 1.3 Impl层 1.4 Mapper层 1.5 测试接口 二、优化 2.1 2.2 一、…