我是从 2015 年开始接触 Golang,并在之后开始在某出行公司的线上环境大规模使用,同时个人还利用个人时间深入研究过 Golang 的底层实现机制,包括内存管理、GC 机制、Runtime Scheduler、Interface、Channel 等。这篇文章力求客观的讨论一下 Golang 的利弊。
优点
1. 简单
简单应该是 Golang 最大的优势。Golang 的语言特性简单,学习周期短,熟悉其他编程语言的开发者基本都可以在短时间学会并写出各方面都还不错的代码。所谓各方面都还不错是说新手开发者写出来的代码和一些有经验的开发者写出来的代码差别并不会太大。
Golang 语言层面上的简洁性让一些新手程序员也能写出性能不错,bug 不多的程序,这个相比其他高级语言,比如 C++,是一个非常大的提升。MIT 的一个非常有名的课程 6.824 最开始使用功能的 C++,后来改成了 Golang,就是为了让大家可以专注于分布式算法本身,而不是陷入到语言细节的调试当中去。
2. 兼顾开发效率和性能
Golang 由于丰富的原生库和周边生态的支持,开发效率甚至可以比肩 Python。很多公司早期,或者项目早期的时候为了赶开发进度都会将开发效率放在第一位,比如 Python,PHP 这种动态语言。但是动态语言的性能劣势非常明显。现在的一个好现象就是 Golang 已经越来越多的被小公司采用了,毕竟写一个 http server 不过三行代码。
另外在开发效率的前提下,Golang 还具有非常高的性能。这一方面得益于静态语言,另一方面和其本身的语言设计也有很多关系。但是这里说的非常高的性能有点不太严谨,相比 C++/Java 这种老牌的高级语言,在某些场景下的 benchmark 还是要略逊一筹的。
3. 语言级别的特性支持
所谓语言级别的并发支持,就是使用 go func 直接启动一个 goroutine,外加 select/chan 等周边。在没有语言级别的支持之前的异步编程简直就是 callback 噩梦。记得云风大神之前对 Golang 的一段评价:
我发现我花了四年时间锤炼自己用 C 语言构建系统的能力,试图找到一个规范,可以更好的编写软件。结果发现只是对 Go 的模仿。缺乏语言层面的支持,只能是一个拙劣的模仿。—— 云风
现在很多人使用一门新语言的时候,有时候还会问:“有对应的 coroutine 库吗?” Golang 的语言层面的支持极大的解放了开发者的心智负担。
缺点
1. runtime
支持 runtime 的编程语言一个无法绕开的问题就是 runtime 带来的一系列问题,比如性能损耗。在 rust 语言介绍自己的优势的时候有一点就是 no runtime。
Golang 的线程模型调度是 M:N,runtime 调度模型是 GMP 模型,伪抢占式的。简单点来说就是 runtime scheduler 可以类比成操作系统,但是缺乏硬件层面上对操作系统的支持,比如硬件中断,这就对 sheduler 的设计要求的非常高,但是 Golang 的实现并没有想象中的那么好。
2. 并不能做到真正高并发高性能
Golang 的高并发使用原生库来实现的话一般都是通过多 goroutine + select/channel,但是我们看 channel 源码,发现这个东西就是一个队列+一把锁。这也就意味着无法避免多个 goroutine 带来的竞争问题。我之前测试过在多个 goroutine 竞争同一个 channel 的时候,性能急剧下降。所以很多高性能的高并发程序如果是用 Golang 来写,很多都会避免使用 channel 来传递数据,而是借用类似 disruptor 的 ringbuffer 技术。
但是这并不是说 Golang 在高并发场景下性能不行,对于日常的 io 密集型的 web server,可以说性能是足够了。
其他
这里谈一下 Golang 自问世以来一直被诟病的几个问题。
1. GC
大概从 1.0 版本以来,GC 就一直被诟病。值得欣慰的是,Golang 的 GC 一直在发展,基本在每个版本都有一定的改进。1.8 版本是 GC 的一个里程碑,使用并发三色标记法的 GC 算法的stw 时间甚至达到了微秒级。目前社区貌似在讨论分代 GC 的方案,这个后面专门写一篇文章细说。
2. 包管理
包管理也是一直被诟病,主要是一直没有一个官方的解决方案。直到去年官方终于开始有行动了,推出了 module,相对来说还是一个很不错的方案。
3. 泛型
Golang 没有支持泛型的很大一个原因是泛型太复杂。尽管很多人说 interface 也能实现泛型功能,但是这个泛型还是有一些本质的区别的。没有泛型确实是一个减分项。
全部0条评论
快来发表一下你的评论吧 !