在码农世界里,有几个迷之争论:Vim/Emacs与IDE之争、Tab与Space之争、Windows与Linux(也包括macOS等)之争,诸如此类。任何一个争论都是莫名激烈、动不动就友尽,如果你恨一个人,就把他拖进这些争论吧。
其中最可能流血、最板砖横飞的争论,无疑是语言之争(当然,“PHP是世界上最好的语言”这是毋庸置疑的)。使用某个语言或者不使用某个语言,都可能轻易引发激烈的争吵,需要面对巨大的宗教、舆论压力,因此切换语言对码农来说,几乎就是死生之大事。
最近做了一件让人紧张、脸红、流汗的事:重新改写miniSIPServer产品的业务引擎部分,从Python语言切换到Lua语言。作为熟悉(非精通)Python多年的开发人员,离开自己的舒适区去面对新的挑战,去填新的坑,内心还是蛮纠结的。这个切换工作花费了几个月的时间,排除拖延症导致的时间浪费,如此耗时也是近几年罕见,通常一个复杂点的特性也就几个星期工作量而已。在产品博客上,已经简要描述了这次切换的原因,本文从开发人员视角再补充一些细节。
先泛泛了解一些Python和Lua的共性部分:都是脚本类、解释型语言;都是比较简单、干脆的语法;通常都会应用于粘合性场景。常见的不同之处主要在于:Lua更精干、小巧、速度更快,而Python是通用性、全栈型语言,有更广泛的库支持。Python即可适合开发独立的应用程序、也适合作为胶水语言嵌入到应用中,而Lua基本功能集很小,通常只适合作为辅助型语言使用,不太适合作为独立应用的开发语言使用。
从上面这些简要描述来看,Python语言的应用领域要比Lua广泛得多。实际上简单搜索一下可以发现,Lua仅仅在网络、游戏等领域才有一些应用范例。
离开具体应用场景来谈论语言的优劣就是耍流氓,在考虑、考察了很久以后,最终还是重新选择了Lua,这个决定过程是逐步养成的。
我们自己在设计、开发产品时,总是会狂热地追求简单、精致、高效等特性,从产品名称也可以看出我们对『小巧』的迷恋。将Python及支持库打包进安装包,有数MB之大,而Lua仅仅200KB而已,完成整体打包后,体积能缩小1/3,用户下载我们应用程序的时间相应也能减少1/3!这点从一开始就很吸引我们。
然而最初我们选择的是Python,因为Python的库实在太方便了!例如其中的smtp库、xml库等,在我们产品初期的确帮了大忙。而随着我们自己逐步在MSS的核心自行开发了这些基础库,重新审视后发现:Python的库对我们的产品不再是必须的了。这也就是我们重新思考的一个触发点。
如果说Python是屠龙刀,那Lua就是铅笔刀。如果仅仅是削铅笔,那显然铅笔刀比屠龙刀更合适。对于我们而言,应用脚本语言更多的是考虑对核心功能的封装,以及基于此编写业务逻辑脚本,以脚本语言的动态性适配客户需求的多样性。因此在核心功能完备的情况下,脚本语言越轻巧越好,越简单越好,Lua太适合这种场景了。
Python另外一个让人比较诟病的地方就是GIL。由于GIL的存在,Python无法实现真正的多线程。如果用一个Python实例来满足多种业务的需求,一旦其中一个业务出现问题,整个Python虚拟机可能会锁死,导致整个系统崩溃。如果采用多个Python实例,Python的VM实在太重了,对系统的设计、实现、工作负荷都会造成很大的困扰。
而在这点恰恰发挥出Lua的优势。Lua的VM非常小巧,以至于可以毫不犹豫地启动多个VM来实现业务。实际上,我们最终实现为『一个业务启动一个Lua的VM』。具体设计、实现时不用再考虑资源锁死的问题,而且Lua提供的coroutine也很棒,很多场合下可以轻松满足高性能的要求。启动多个Lua的VM带来的最大好处就是:系统的稳定性有了进一步的提高。事实上相当于每个业务都处于沙盒之中,一旦某个业务出现异常,其他业务可以完全不受影响,即使是对已出问题的业务实例,将沙盒(VM)抹掉即可。单纯这一个特点就足以让我们决定放弃Python,改用Lua了。
而Lua带来的第二个好处是『热更新』特性。在运营级、大型企业级软件系统中,『热更新』是非常重要的特性,可以确保整个系统尽可能地稳定运行。而由于每个业务都由独立的VM运行,因此我们可以做到动态修改业务脚本。实际上,我们在开发新业务引擎时,常常在保持MSS运行的情况下,修改脚本测试各种业务逻辑,效果良好。
Lua比Python简单很多,不做特殊优化,Lua的速度比Python要高出很多。我们在开发测试过程中发现,采用Lua脚本对系统的性能影响不大,比Python要节省资源。MSS作为一个高性能SIP服务器,核心采用C、C++实现,而某些处理过程也有客户化、定制化的需求,更关键的是也存在『热更新』的需求,因此我们甚至希望在考察Lua业务引擎效果的基础上,考虑替换MSS呼叫核心部分的处理逻辑,用Lua实现部分呼叫引擎。
因为简单,所以我们可以比较随意地直接修改Lua的代码,以便满足我们应用场景的一些特殊需求。而面对Python,我们可不敢这么做。
当然Lua也有明显的缺点,『简单』在某种程度上是『简陋』的同义词。很多情况下需要扩展一些功能给Lua使用,另外就是Lua也存在不少的坑,即使是Python和C/C++老手也需要小心翼翼。
最后,老话说得好:“结合应用谈语言”。没有完美的语言,只有适合你应用的语言。当你不知道该怎么选择时,选择Python、Java这类通用型语言基本无风险,正如我们在项目早期作出的选择一样,在当时都是非常正确的。只是我们要与时俱进而已。