我是在几个月前学习Golang的,这要感谢@normanmaurer和@MegOnWheels的提议!倒不是因为我想要抹黑Scala和JVM,而是由于它们在将近十年后开始显得很糟糕。
为什么JVM开始显得很糟糕?
我当初开始使用JVM时,对于应用程序及其虚拟机/运行时环境彼此分开来感到很高兴。在几乎专职编写了9年的Scala代码后,我对它逐渐厌恶起来。原因何在?
因为JVM方面的差异让我开发出易于预测(言外之意:稳定)的应用程序极其困难。一个版本这么做,下一个版本搞坏了它,所以从一位优秀程序员的角度来看,你不得不另想办法,避开运行时环境方面的问题和功能。
其次,为了使用最新的功能特性,比如TLS服务器名称指示(SNI)――TLS1.3问世之后,该特性其实不是最先进的,你就要确保JVM/运行时环境是最新版本,无论想在哪里使用该特性,就得如此(TLSSNI是Java7->8)。
如果你是根本不用肩负运行职责的程序员,这可能对你来说是可以接受的,但是我不得不关注自己编写的代码的运行,就跟我不得不关注代码本身那样!
那么什么让golang在我看来如此出色?
你得到了静态链接的二进制文件。没有运行时环境,根本不用安装任何东西。
从部署的角度来看这尤为出色,因为你只要操心你的二进制文件及其资产(要是有的话)。
另外值得一提的是,由于我的Scala/Java.jar包(捆绑了所有依赖项)很少小于60MB,加上不小于500MB的JVM,这样一来浪费了大量的磁盘空间,许多方面需要定期更新。我的golang二进制文件却总共很少超过13MB。
最后但并非最不重要的一点是,scala-sbt这个构建工具糟糕透顶。真是这样。在我看来,它是人类有史以来所想出的一款最糟糕的构建工具!经常破坏向后兼容性,需要我处理新插件,真是糟透了!
我想要一款专门构建代码,并生成一种有用的二进制文件格式的构建工具。
这是“可靠”的工具实际上要做的事情。除了超级丰富的功能(比如测试、模糊以及所有这种优秀特性)外,它还要可靠地构建代码,又不需要我精心维护配置文件!到目前为止,简单的Makefile文件足以满足我的所有要求。
另外,我之前在Scala/JVM上需要Disk-Space时,rm-rf ~/.ivy2基本上解决了这个问题,因为你从sbt获取的所有依赖项jar包都驻留在此。但是一旦你这么做,你可能会另谋职业,因为一些工件/jar包可能不复存在,因而破坏你构建的代码。与之相反,在Golang中,我只要使用gitclone命令把依赖项源代码克隆到我的代码库,将它作为子模块添加到git,或者直接使用git,添加依赖项代码即可。
Scala二进制不兼容性(对原始文章的后续更新)
许多人指出,拥有二进制依赖项缓存几乎就跟拥有源代码一样好。
有没有遇到过多个Scala版本的情况?或者在Scala这个领域待的时间太短,不知道Scala二进制不兼容性?如果你喜欢这种东西,它们确实很出色。但是我不喜欢。我不想查出某某软件包这样的所有依赖项:它们只能在Scala2.9上运行,但需要为你的2.10项目、2.11或其他版本的项目重新编译。
inline错误修复(同样在原始文章发表后补充上去)
我不知道你们是怎么一个情况,反正我喜欢修复我使用的别人代码中的错误。
看到别人得益于我的代码,这让我有满满的自豪感,也让我感到很高兴。
所以,只要我不得不查明Scala/JVM方面的问题,我通常采取的程序就是下载该库源代码。然后试图让那个开发人员所用的构建工具运行起来。有时候这个工具是sbt,有时候是ant,有时候是maven,有时候是我听都没听说过的某个工具。听起来很好吗?
现在,我要花时间让这个工具运行起来,然后才能花时间来实际修复代码。
浪费时间啊!
如果你已经有源代码,如果我已经为目前的版本对它们进行了编译,如果你只要找到某一行代码,改动它,然后测试代码,岂不是要容易得多?
还是说,你宁可经历那个维护人员的构建工具的整个构建过程,把因而获得的.jar包放到你的缓存中,或者部署它,然后可能再次下载它,不得不改动构建的代码以便使用这个新的工件?
从简单的逻辑角度来看,我总是会选择第一个,因为它让我避免了许多头痛的问题,让我得以专注于手头的问题。
交叉编译
诚然,JVM上的这个问题还没有到你为自己的平台搞一个切实可行的Java运行时环境(JRE)的地步。在我看来,让无比庞大的JVM在你的RaspberryPI上运行也许不是使用其CPU的最佳方法。
那么,go如何处理这个问题呢?罗布·派克(RobPike)有一场精彩的演讲,介绍了go编译器的内部原理,为我们作了清楚的解释。自go1.7以来,你再也没有必要使用C这种语言了,而是可以让golang径直由Go编译成ASM。太棒了!
所以,为了在OSX上为我的RaspberryPI交叉编译一些纯粹的go代码,我只要运行:
GOOS=freebsd GOARCH=arm GOARM=6 go build src/*.go
就是这样。用scp命令传输该二进制文件,结果令人满意。何不在ARM本身上面这么做呢?a)在我的英特尔i7八核处理器上所花的时间长得多,b)ARM上的golang最新只能适用于版本1.4,因为新版本会有一些问题,但是使用1.8-HEAD交叉编译完全很好。
性能
从我头几个月在生产环境下使用golang的情况来看,我可以证实,就本人的使用场合(主要是网络节点)而言,golang速度极快,尽管零拷贝机制(Zero-Copy)在FreeBSD上还未得到支持。
对我的应用程序而言,内存耗用只是原来JVM项目的大概十分之一,因而在我们的数据中心操作过程中降低了对内存的要求,因而之前使用的JVM内存中大约十分之六从我们的FreeBSD虚拟机中释放出来,因而为我们新的客户机/应用程序腾出了大量资源。
结束语
Golang会成为我新的主要语言,Scala只是偶尔用一下,用于需要之前由我开发的软件得到支持的现有客户机。