从Java转向Go——AeroFS的一段奇妙之旅
AeroFS是一家为企业提供安全和廉价的私有云存储方案。日前,AeroFS的开发团队发现程序内存发现异常(AeroFS应用大部分是基于Java开发的),经过排查后发现JVMs之间无法进行更多的只读内存共享,于是决定采用新的开发工具,选用Go语言,随后,代码减半、常驻内存下降、Docker镜像内存也减少许多。AeroFS开发团队将这一事件记录下来,我们一起来看下。
AeroFS应用由很多微服务构成,主要以Java来编写。在一段时间里,它运作良好,服务的用户约为几千。但当我们转到Docker时,发现程序内存占用出现大幅攀升。于是前期我们先是使用了Docker监控工具进行检查,最后得出如下的检测脚本:
for line in `docker ps | awk '{print $1}' | grep -v CONTAINER`; do \ echo $(( `cat /sys/fs/cgroup/memory/docker/$line*/memory.usage_in_bytes` / 1024 / 1024 ))MB \ $(docker ps | grep $line | awk '{printf $NF" "}') ; \done | sort –n
运行后输出结果是运行中的容器清单,以常驻内存大小进行排序。例如:
- 46MB web
- 66MB verification
- 74MB openid
- 82MB havre
- 105MBlogcollection
- 146MB sp
- 181MB Sparta
该分析没有覆盖那些内存占用异常高的Java服务,因为这是非常规的。我们总结了几点造成内存占用高的因素:
- 由于tomcat servlet被放置于单独的容器上,造成JVMs的增加。
- JVMs之间无法进行更多的只读内存共享:原因是所有共享库都依赖于JVM本身,以及很多JARs是被多服务所使用的。
- 内存分离会导致过多的批量启发,从而使由服务分配的缓存增多。
有鉴于此,我们决定使用新的工具。
代码名字:Greasefire
随后我把该为AeroFS程序减负的项目作为黑客松参赛项目,我的竞赛点是:
- CPU占用下降
- 保持内存的稳定和安全
- 常驻内存占用减少一倍甚至更多
新工具尝试
为了实现CPU和内存的优化目标,选择的语音需要以系统编程为设计根本。经过一番筛选后,Go和Rust成为了我的目标。这两者都能很方便地编译出能在最小型容器运行的小型静态二进制代码,都具有良好的性能表现,安全性以及并发处理支持,最关键的是比JVM有着更少的内存占用。
Rust的类型系统特性非常迷人。但是它与Go相比还是显得稚嫩,同时支持库在HTTP和低阶网络编程上有所欠缺。
与C家族相近的Go入门较易,但是有很多独有的特性需要结合支持文档进行细读。幸好支持文档写得详尽而细致,与标准库资源无缝连接,所以能够使初学者写出清晰可读性高的代码。此外,易于与文本编辑器如vim等整合的gofmt工具的提供,也是很令人兴奋的。
结果
- 代码缩减了约一半,从175行减少为96行。
- 常驻内存从87MB下降为3MB,减少了将近29倍。
- Docker镜像大小从668MB减少为4.3MB。
在黑客马拉松的最后一天,我还对CA(Certificate Authority 认证授权)进行了优化。我们的认证服务先是从内部服务和桌面用户机器上接收证书签名请求,然后以P2P方式在用户和服务器间返回相关签名认证。当我以新CA替换原来的Java时,常驻内存更是减少了100倍。最后,该项目获得了黑客松的技术卓越奖。
原文链接:Aerofs