对于这里面测试的框架,影响因素主要如下:
1. zero copy 和内存复用: 内部处理字节的0 拷贝(go 官方http 库为了减少开发者的出错概率,没有使用zero copy,否则开发者可能在无意中引用了已经放回buff 池内的的数据造成没有意识到的并发问题等等),而内存复用,大部分框架或多或少都已经做了。
2. prefork:注意到go 框架中有使用了prefork 进程的方式(比如fasthttp-prefork),这是fork 出多个子进程,共享同一个listen fd,且每个进程使用单核但并发(1 个P)处理的逻辑可以避免go runtime 内部的锁竞争和goroutine 调度的消耗(但是go runtime 中为了并发和goroutine 调度而存在的相关“无用”代码的消耗还是会有一些)
3. 语言本身的性能差异,对于第一点,其实简化了各种编解码和路由之后,虽然提高了性能,但是往往会降低框架的易用性,对于一般的业务而言,不会出现如此高的QPS,同时选择框架的时候往往还需要考虑易用性和可扩展性等,同时还需要考虑到公司内部原有中间件或者SDK 所使用的框架集成复杂度。
对于第二点,如果是作为一个网络代理而言,没有业务方的开发,往往可以使用真正的完全zero copy,但是作为业务开发框架提供出去的话是需要考虑一定的业务出错概率,往往牺牲一部分性能是划算的。
prefork ,java netty 等是直接对于线程操作,可以更加定制化的优化性能,而go 的goroutine 需要的是一个通用协程,目的是降低编写并发程序的难度,在这个层次上难免性能比不上一个优化的非常出色的Java 基于线程操作的框架;但是直接操作线程的话需要合理控制好线程数,这是个比较头疼的调优问题(特别是对于新手来说),而goroutine 则可以不关心池子的大小,使得代码更加优雅和简洁,这对于工程质量保障其实是一个提升。另外这里存在prefork 是由于go 没法直接操作线程,而fasthttp 提供了prefork 的能力,使用多进程方式来对标Java 的多线程来进一步提高性能。
语言本身来说Java 还是更加的成熟,包括JVM 的Jit 能力也使得在热代码中和Go 编译型语言的差异不大,何况Go 本身的编译器还不是特别成熟,比如逃逸分析等方面的问题,Go 本身的内存模型和GC 的成熟度也比不上Java。还有很重要的一点,Go 的框架成熟度和Java 也不在一个级别,但相信这些都会随着时间逐步成熟。