服务端I/O性能大比拼:Node、PHP、Java和Go(三)
服务端I/O性能大比拼:Node、PHP、Java和Go(三)
谎言,诅咒的谎言和基准
对这些各种模式的上下文切换进行准确的定时是很困难的。也可以说这对你来没有太大作用。所以取而代之,我会给出一些比较这些服务器环境的HTTP服务器性能的基准。请记住,整个端对端的HTTP请求/响应路径的性能与很多因素有关,而这里我放在一起所提供的数据只是一些样本,以便可以进行基本的比较。
对于这些环境中的每一个,我编写了适当的代码以随机字节读取一个64k大小的文件,运行一个SHA-256哈希N次(N在URL的查询字符串中指定,例如.../test.php?n=100),并以十六进制形式打印生成的散列。我选择了这个示例,是因为使用一些一致的I/O和一个受控的方式增加CPU使用率来运行相同的基准测试是一个非常简单的方式。
关于环境使用,更多细节请参考这些基准要点。
首先,来看一些低并发的例子。运行2000次迭代,并发300个请求,并且每次请求只做一次散列(N = 1),可以得到:
![](/uploads/article/2017/08/12/20170812115913_3166.webp)
时间是在全部并发请求中完成请求的平均毫秒数。越低越好。
很难从一个图表就得出结论,但对于我来说,似乎与连接和计算量这些方面有关,我们看到时间更多地与语言本身的一般执行有关,因此更多在于I/O。请注意,被认为是“脚本语言”(输入随意,动态解释)的语言执行速度最慢。
但是如果将N增加到1000,仍然并发300个请求,会发生什么呢 —— 相同的负载,但是hash迭代是之前的100倍(显着增加了CPU负载):
![](/uploads/article/2017/08/12/20170812120018_8148.webp)
忽然之间,Node的性能显着下降了,因为每个请求中的CPU密集型操作都相互阻塞了。有趣的是,在这个测试中,PHP的性能要好得多(相对于其他的语言),并且打败了Java。(值得注意的是,在PHP中,SHA-256实现是用C编写的,执行路径在这个循环中花费更多的时间,因为这次我们进行了1000次哈希迭代)。
现在让我们尝试5000个并发连接(并且N = 1)—— 或者接近于此。不幸的是,对于这些环境的大多数,失败率并不明显。对于这个图表,我们会关注每秒的请求总数。越高越好:
![](/uploads/article/2017/08/12/20170812120257_5766.webp)
每秒的请求总数。越高越好。
这张照片看起来截然不同。这是一个猜测,但是看起来像是对于高连接量,每次连接的开销与产生新进程有关,而与PHP + Apache相关联的额外内存似乎成为主要的因素并制约了PHP的性能。显然,Go是这里的冠军,其次是Java和Node,最后是PHP。
### 结论
综上所述,很显然,随着语言的演进,处理大量I/O的大型应用程序的解决方案也随之不断演进。
为了公平起见,暂且抛开本文的描述,PHP和Java确实有可用于Web应用程序的非阻塞I/O的实现。 但是这些方法并不像上述方法那么常见,并且需要考虑使用这种方法来维护服务器的伴随的操作开销。更不用说你的代码必须以与这些环境相适应的方式进行结构化; “正常”的PHP或Java Web应用程序通常不会在这样的环境中进行重大改动。
作为比较,如果只考虑影响性能和易用性的几个重要因素,可以得到:
语言线程或进程非阻塞I/O易用性
| PHP | 进程 | 否 | | |
| - | | | | |
| Java | 线程 | 可用 | | 需要回调 |
| Node.js | 线程 | 是 | | 需要回调 |
| Go | 线程(Goroutine) | 是 | | 不需要回调 |
线程通常要比进程有更高的内存效率,因为它们共享相同的内存空间,而进程则没有。结合与非阻塞I/O相关的因素,当我们向下移动列表到一般的启动时,因为它与改善I/O有关,可以看到至少与上面考虑的因素一样。如果我不得不在上面的比赛中选出一个冠军,那肯定会是Go。
即便这样,在实践中,选择构建应用程序的环境与你的团队对于所述环境的熟悉程度以及可以实现的总体生产力密切相关。因此,每个团队只是一味地扎进去并开始用Node或Go开发Web应用程序和服务可能没有意义。
事实上,寻找开发人员或内部团队的熟悉度通常被认为是不使用不同的语言和/或不同的环境的主要原因。也就是说,过去的十五年来,时代已经发生了巨大的变化。