1. 初识
初次接触golang是因为我们业务上有一个比较扯淡的需求,要给N多台不同的vm上每一台安装一个agent,用来采集监控指标和日志内容,这种需求估计绝大部分互联网从业者都见过。目标机上好几种不同的操作系统让人想到之前做过的类似watchdog的很多插件,于是第一眼就排除了C。其实后来选择Go也是自己一厢情愿,小小的利用了一把自己手中的技术决策权,本来很多竞品都是用python写的,还有用java写的,由于自己对Go有过一些了解,并且在当时Google发的一份报告中,统计了目前为止市面上用go写的项目都是什么,其中agent类占了大头。于是决定毫不犹豫的入坑。
2. 蜜月期
golang在最初的几个月带给我们的惊喜是无法言语的。虽然到现在还有很多人诟病它反常理的语法,但是在交叉编译,轻量级goroutine,duck typing,编译飞速,以及每一个版本GC对stop the world的不断优化都让我们尝到了很多甜头。
在agent的开发过程中,其实go带来的最大的优势就是两个:
语言层面对channel的支持。
- 交叉编译带来的部署便捷性。
channel在语言层面的实现不能说是一种进步,但是绝对是非常有意义的tradeoff,其实我们在做技术决策的时候,做的多了就会发现,没有什么方案是完美的,最终的结果都是各方面妥协的结果,是当前情况下相对最合适的。channel把几十年前那帮计算机语言创世者提出的通过共享通道交换信息首次实现在了语法里,并且把linux中的select也写在了语法中,不得不说是一种对现世的妥协与低调的进步。利用channel我们把整个agent中的指标收集与发送做成了生产消费模式,极大的简化了代码的架构,维护成本也显著降低。几千行的代码,编译出来不到10MB的二进制文件也成了我们的优势,尤其是对host OS的影响之小(CPU利用率 1%,RAM使用40MB以下),让很多之前用C写过类似agent的人都刮目相看,这个时候我们发现整个团队已经彻底认可了Go,于是决定逐渐把整个后端技术栈转向Go。
3. 没有没有缺点的语言
当我们开始一步一步重构后端的一些不太重要的服务的时候,Go在一些方面的先天不足也慢慢的暴露了出来:
- 缺乏泛型。
- 依赖的老大难问题。
- 开源社区的支持度偏低。
范型的缺乏实际上并不是什么太大的问题,我们完全可以用interface凑合写,但是第二个依赖问题可真是让人无语的存在,好在后来有了vendor这个不算解决办法的解决办法,算是让工程化的多人协作回到了正轨。开源社区的支持度偏低主要是表现在很多常用的轮子没有Go的版本,我们自己只好通过造轮子来弥补,短短几个月自己造了一堆轮子,想想之前用node的时候,想都不想就无脑npm install的日子,有时候会觉得是不是Go并没有那么完美。
4. 用事实说话
轰轰烈烈的重构进行的差不多的时候,我们尝试在生产环境替换一部分节点,虽然之前重构的时候遇到的种种问题让我们有些怀疑Go这个东西,但是当部署完成后看到graphite上面那让人又爱又恨的并发时延降到历史最低,所有人的心情还是非常愉悦的。当然后来进行了比较专业的性能测试,替换后的服务在并发上性能提升了快5倍不止,而资源消耗并没有太大的波动,大家都不再怀疑当初的决定。
5. 永远在路上
“没有造过轮子的程序员不是好的厨子”
由于业务的需求,我们把几个中间件也用Go重新实现了一遍,包括cache和MQ,Go在这些领域的先天优势非常明显:由于生产业务无关性,功能可以做的非常通用,而这些模块先天自身功能和对性能的极致要求就决定了Go这种弱化OOP,并且性能较高的编程语言才是最合适的(这也是为什么redis用C写的,而不是Java)。
几个月时间,我们从一个模块带有python,node,java的杂货铺团队(当然现在业界把这种组织起了个好听的名字叫全功能团队),慢慢过度到只剩下少量java和Go,人员技能更专一的全新团队(带来的管理复杂度和工程合作成本的下降是非常客观的),重要的是,大家都喜欢Go。
回顾过去的不到一年时间,从一个小小的偶然机遇,到最后全量切换技术栈,每解决一个问题所获得的满足感和成就感伴随着对技术最质朴的追求,我们在跌跌撞撞的成长过程中不断的突破自己带来的技能蜕变才是我们最大的收获。