写点什么

记一次获得 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:158797

评论

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

紫光闪存京东品牌焕新日来袭!多重福利精彩放送

新消费日报

使用 Protobuf 实现高效数据交换

左诗右码

protobuf

Docker 安装 KONG 带你玩转 API 网关

左诗右码

Kong 网关

CodeArts加速软件智能化开发,携手HarmonyOS重塑企业应用创新体验

轶天下事

DevSecOps在数字政府建设中的实践研究

EquatorCoco

DevOps 运维 低代码 网络

数业智能亮相AI论坛,共探数字心理健康新领域

心大陆多智能体

智能体 AI大模型 心理健康 数字心理

基于 Groq 和 Cartesia 的高速 AI 语音助手发布;xAI 将自行打造超级计算机丨 RTE 开发者日报

声网

RPA助力企业财税业务智能化转型:深入探索与实践

不在线第一只蜗牛

推荐个人或企业使用的4个虚拟桌面解决方案 – 云桌面

青椒云云电脑

云桌面 云桌面解决方案 虚拟云桌面解决方案

云桌面系统解决方案-青椒云

青椒云云电脑

云桌面 云桌面厂家 云桌面解决方案 云桌面系统

Nginx 高性能架构解析

快乐非自愿限量之名

nginx 运维

华为云CodeArts 12大安全防护机制,端到端全面保障软件供应链安全!

轶天下事

唐刘:当 SaaS 爱上 TiDB(一)- 行业挑战与 TiDB 的应对之道

PingCAP

数据库 SaaS #TiDB 洞察 资源管控

Apache IoTDB & TsFile 智慧能源应用“上会”啦!

Apache IoTDB

你的数据库真的规范吗?小心这些“潜在风险”!

NineData

DevOps 数据库规范 审计日志 NineData SQL 规范

AI“语速”知多少?基于云拨测的国产大模型使用体验测评!

火山引擎边缘云

AI 大模型 云拨测、 云拨测 #大模型

华为云助力徐州市城管局荣获数字城市赛道“百景新锐奖”

新消费日报

从 Icelake 到 Iceberg Rust

Databend

积分经济学指南:掌握加密货币激励的新语言

TechubNews

大模型融入云平台,信息化走向数智化

快乐非自愿限量之名

云平台 大模型 数智化

华为云发布ServiceStage:内置优秀业界实践「云应用管理和运维」模板

轶天下事

用不了ChatGPT?快试试免费又强大的Anthropic Claude

蓉蓉

GPT Claude

利用Altair One 云平台,轻松实现全球企业产品研发创新与优化

Altair RapidMiner

人工智能 软件 数据分析 制造 altair

Persistent在《机构投资者》(Institutional Investor)2024年度亚洲高管团队调查中被评为管理和高管领导力卓越企业

财见

全球销量领先车企基于Serverless服务构建数据实时处理的千万级车联网业务

轶天下事

百度安全大模型智能体实践入选信通院“安全守卫者计划”优秀案例

百度安全

平凯星辰黄东旭出席 2024 全球数字经济大会 · 开放原子开源数据库生态论坛

PingCAP

开源 金融行业 #TiDB 开放原子 平凯星辰

微服务nacos默认开启鉴权JeecgBoot

JEECG低代码

微服务 nacos

深入理解 Nginx 与 Kong 的配置与实践

左诗右码

Kong 网关

lodash凉了!Radash异军突起

大师兄

JavaScript Vue lodash js工具库 radash

华为云Astro Zero低代码平台案例:小、轻、快、准助力销售作战数字化经营

轶天下事

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