AICon 上海站|90%日程已就绪,解锁Al未来! 了解详情
写点什么

记一次获得 3 倍性能的 Go 程序优化实践

  • 2017-09-13
  • 本文字数:3120 字

    阅读完需:约 10 分钟

Go 的高性能真不是吹的,当然是要在足够的优化之后。获得 3 倍性能的优化实践,值得借鉴。

背景介绍

之前公司一直使用 Logstash 作为日志文件采集客户端程序。Logstash 功能强大,有丰富的数据处理插件及很好的扩展能力,但由于使用 JRuby 实现,性能堪忧。而 Filebeat 是后来出现的一个用 Go 语言实现的、更轻量级的日志文件采集客户端。性能不错、资源占用少,但几乎没有任何解析处理能力。

通常的使用场景是使用 Filebeat 采集到 Logstash 解析处理,然后再上传到 Kafka 或 Elasticsearch。值得注意的是,Logstash 和 Filebeat 都是 Elastic 公司的优秀开源产品。

为了提高客户端的日志采集性能,又减少数据传输环节和部署复杂度,并更充分地将 Go 语言的性能优势利用于日志解析,于是决定在 Filebeat 上通过开发插件的方式,实现针对公司日志格式规范的解析,直接作为 Logstash 的替代品。

实现与优化

Version 1.0

先做一个最简单的实现,即用 Go 自带的正则表达式包 regexp 做日志解析。性能已经比 Logstash(也是通过开发插件做规范日志解析)高出 30%。

这里的性能测试着眼于日志采集的瓶颈——解析处理 环节,指标是在限制只使用一个 CPU core 的条件下(在服务器上要尽量减少对业务应用的资源占用),采集并解析 1 百万条指定格式和长度的日志所花费的时间。

测试环境是 1 台主频为 3.2GHz 的 PC。为了避免 disk IO 及 page cache 的影响,将输入文件和输出文件都放在 /dev/shm 中。对于 Filebeat 的 CPU 限制,是通过启动时指定环境变量 GOMAXPROCS=1 实现的。

这一版本处理 1 百万条日志花费的时间为 122 秒,即每秒 8200 条日志。

Version 2.0

接下来尝试做一些优化,看看这个 Go 插件的性能还可不可以有些提升。首先想到的是替换 regexp 包。Linux 下有一个 C 实现的 PCRE 库, https://github.com/glenn-brown/golang-pkg-pcre 这个第三方包正是将 PCRE 库应用到 Golang 中。CentOS 下需要先安装 pcre-devel 这个包。

这个版本的处理时间为 97 秒,结果显示比第一个版本的处理性能提升了 25%。

Version 3.0

第三个版本,是完全不使用正则表达式,而是针对固定的日志格式规则,利用 strings.Index() 做字符串分解和提取操作。这个版本的处理时间为 70 秒,性能又大大的提升了将近 40%。

Version 4.0

那还有没有进一步提升的空间呢?有。就是 Filebeat 用作序列化输出的 JSON 包。我们的日志上传使用 JSON 格式,而 Filebeat 使用 Go 自带的 encoding/json 包是基于反射实现的,性能一直广受诟病。如果对 JSON 解析有优化的话,性能提高会是很可观的。

既然我们的日志格式是固定的,解析出来的字段也是固定的,这时就可以基于固定的日志结构体做 JSON 的序列化,而不必用低效率的反射来实现。Go 有多个针对给定结构体做 JSON 序列化 / 反序列化的第三方包,我们这里使用的是 easyjson https://github.com/mailru/easyjson。

在安装完 easyjson 包后,对定义了日志格式结构体的程序文件执行 easyjson 命令,会生成一个 xxx_easyjson.go 的文件,里面包含了这个结构体专用的 Marshal/Unmarshal 方法。

这样一来,处理时间又缩短为 61 秒,性能提高 15%。

这时,代码在我面前,已经想不出有什么大的方面还可以优化的了。是时候该本文的另一个主角,火焰图出场了。

on-cpu/off-cpu 火焰图

火焰图是性能分析的一个有效工具, http://www.brendangregg.com/flamegraphs.html 这里是它的说明。通常看到的火焰图,是指 on-cpu 火焰图,用来分析 CPU 都消耗在哪些函数调用上。

on-cpu 火焰图

安装完 FlameGraph https://github.com/brendangregg/FlameGraph 工具后,先对目前版本的程序运行一次性能测试,按照说明抓取数据生成火焰图如下。

:FlameGraph 对于 C/Go 程序是通用的。对于 Go 程序,也可以使用自带的 net/http/pprof 包作为数据源,然后安装 Uber 的 go-torch https://github.com/uber/go-torch 工具来自动调用 FlameGraph 脚本生成 on-cpu 火焰图,执行会稍为简便一些。参见 go-torch 说明。

(点击放大图像)

图中纵向代表的是函数调用栈,横向各个方块的宽度代表的是占用 CPU 时间的比例,需要留意的是靠近顶端的大长条。方块的颜色是随机的没有实际意义。

从上图可以看到 CPU 时间占用最多的主要有两块。一块是 Output 处理部分,稍为大头的是 JSON 处理,这块已经优化过没什么可以做的了。另一块就比较奇怪了,是 common.MapStr.Clone() 方法,居然占了 40% 的 CPU 时间。再往上看,主要是 Errorf 的处理。一看代码,马上明白了。

(点击放大图像)

common.MapStr 是在 pipeline 中存放日志内容的结构体,它的 Clone() 方法实现里判断一个子键值是否为嵌套的 MapStr 结构时,是通过判断 toMapStr() 方法是否返回 error。从这里看,生成 error 对象的代价是非常可观的。于是,一个显然的 fix,就是将 toMapStr() 中的判断方法移到 Clone() 中并避免生成 error。

Version 5.0

对修改后的代码重新生成一张火焰图如下。

(点击放大图像)

这时 common.MapStr.Clone() 从图中已经几乎找不见了,证明花费的 CPU 时间已经可以忽略不计。

测试时间一下子缩短到了 46 秒,节省了 33%,非常大的改善!

off-cpu 火焰图

到现在,还有一个之前未提到的问题没有解决——在限制使用一个 core 之后,测试运行时 CPU 利用率只能跑到 80% 多。是不是由于有锁存在影响了性能呢?

这时候,又该请 off-cpu 火焰图 出场了。off-cpu 火焰图,是用来分析程序没有有效利用 CPU 的时候,消耗在什么地方了,在 http://www.brendangregg.com/FlameGraphs/offcpuflamegraphs.html 有详细的介绍。

数据收集比 on-cpu 火焰图要复杂,可以使用大名鼎鼎的春哥提供的 openresty-systemtap-toolkit https://github.com/openresty/openresty-systemtap-toolkit 包。春哥的项目页面中没有详细说明的是 kernel-develdebuginfo 包的安装方法。在此也记录一下。

(点击放大图像)

安装完后按照说明生成了 off-cpu 火焰图如下:

(点击放大图像)

可以明显地看到,对 Registry 文件(Filebeat 用于记录文件采集列表和 offset 数据)的写操作占了一定比例。于是,尝试将 Filebeat 的 spool_size(每完成这么多条日志更新一次 Registry 文件)设置为 10240,默认值的 5 倍,运行测试 CPU 已经可以跑到 95% 以上。而将 Registry 设置到 /dev/shm/ 下也同样可以解决测试时 CPU 跑不满的问题。

这就否定了上面对锁使用不当影响性能的猜测。在实际应用时 spool_size 的设置应当依据结合了 output 端(如写入到 Kafka)的测试数据来决定。

至此,优化结束,性能达到了最初版本的 3 倍!

各个版本的具体运行性能数据如下图所示:

(点击放大图像)

需要稍作说明的是:

  • Filebeat 开发是基于 5.3.1 版本,Go 版本是 1.8
  • Logstash 的测试通过 -w 1 参数配置使用一个工作进程,并未限制使用一个 core
  • 执行时间包括了程序的启动时间(Logstash 的启动时间有将近 20 秒)

最终的优化结果是,针对特定格式和长度的日志解析能力在 PC 上达到了每秒 25000 条,即使在 CPU 主频较低的生产服务器上,也可以达到每秒 20000 条。

Go 的高性能真不是吹的,当然是要在足够的优化后:)

最后,关于 Go 的性能有一篇这样的讨论,有兴趣可以看看.

作者介绍

潘卫华 (Peter),唯品会架构师,关注基础架构方向。10 余年通信及IT 研发经验,曾任职爱立信,现任职唯品会基础架构部门。近年对基于ELK 的日志存储系统,以及基于Spark Streaming 的实时日志分析等技术有较多研究。喜欢对问题进行深入分析和创新思考,善于挖掘各种工具的潜力。


感谢雨多田光对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-09-13 17:159104

评论

发布
暂无评论
发现更多内容

开源啦!!!基于鸿蒙ArkTS封装的图表组件《McCharts》,大家快来一起共创

莓创技术

电机行业MES系统功能介绍及实施方案

万界星空科技

mes 万界星空科技mes 电机行业 电机MES 电机工厂

专访金融时报中文网总编:你怎么看 Crypto?

TechubNews

crypto 加密市场 市场趋势

信创生态新突破:嘉为蓝鲸OpsPilot携手昇思重构智能运维体系

嘉为蓝鲸

运维 嘉为蓝鲸 #WeOps

嘉为蓝鲸深度参与国家DOMM标准实战培训体系构建

嘉为蓝鲸

嘉为蓝鲸 一体化运维 DOMM

鸿蒙生态新基建!CPack制品库实现全链路原生支持

嘉为蓝鲸

DevOps 制品库管理 嘉为蓝鲸

一文探索物流CEO大屏及供应链大屏是如何做好双11保障

京东科技开发者

供应链大屏设计实践

京东科技开发者

多种设备皆可联接!华为云Flexus云会议开箱即用、轻松上手

轶天下事

AI赋能的春天!嘉为蓝鲸3.13发布DeepSeek加持研运新品

嘉为蓝鲸

产品发布 嘉为蓝鲸 DeepSeek

Hologres 计算组实例&分时弹性入门实践

阿里云大数据AI技术

大数据 Serverless 实时数仓 hologres

5 分钟搞定 Golang 错误处理

俞凡

golang

从曼哈顿到龙隐洞天,XeSS 助你决胜游戏战场

E科讯

软件测试丨智能遍历与模型驱动测试

测试人

人工智能 软件测试

「Trading星周二」 Space精华回顾 | 牛市启动倒计时?五大关键议题激辩与未来展望

TechubNews

加密市场 经济趋势 交易分析

数字化人类:人工智能能否复刻大脑的每个细节?

天津汇柏科技有限公司

人工智能 AI

人工智能丨领域建模与知识图谱测试用例生成

测试人

人工智能 软件测试

官网购买、注册即用!华为云Flexus云会议让云上沟通更高效

轶天下事

高清、高效、高性价比,华为云Flexus云会议专为中小企业而生!

轶天下事

突破银行 SRE 转型困境,SRE 与 DevOps 团队协作策略

嘉为蓝鲸

DevOps SRE 银行运维

KubeEdge边缘设备管理系列(四):Mapper-Framework视频流处理

华为云原生团队

云计算 容器 云原生

浏览器崩溃的第一性原理:内存管理的艺术

京东科技开发者

Java 转 Go 秘诀大放送!

CodeBuddy

京东商品详情API接口全攻略

tbapi

京东商品详情数据接口 京东API接口 京东商品详情接口

记一次获得3倍性能的Go程序优化实践_语言 & 开发_潘卫华_InfoQ精选文章