百万 Go TCP 连接的思考3: 正常连接下的吞吐率和延迟

这一篇文章介绍了I/O密集型服务器和计算密集型的服务器的两种场景,对多epoller服务器和goroutine-per-connection服务器两种服务器进行测试,连接数分别是5000、2000、1000、500、200和100。

第一篇 百万 Go TCP 连接的思考: epoll方式减少资源占用
第二篇 百万 Go TCP 连接的思考2: 百万连接的吞吐率和延迟
第三篇 百万 Go TCP 连接的思考: 正常连接下的吞吐率和延迟

相关代码已发布到github上: 1m-go-tcp-server

前两篇的是有巨量连接的情况下服务器的性能,这类服务器可能应用于消息推送、IOT、页游等场景,追求的是大量连接,并发量相对不大的场景。还有一类场景是服务器的连接数不多,几十几百,最多几千的TCP连接,比如公司内的服务之间的调用等,这类服务器在不同的实现下的性能是怎样的?

测试区分两个场景: I/O密集型和计算密集型。I/O密集型的服务比如文件的读取、数据库的访问,远程服务的调用等等,计算密集型的访问比如区块链的挖矿、算法的计算、类似redis这样的基于内存的数据处理服务等等(当然redis还是memory bound类型的服务)。

我们通过time.Sleep让goroutine休眠来模拟I/O密集型的服务,实际goroutine休眠和真正的I/O密集型的服务还是有区别的,虽然它们都有一定的耗时,goroutine在等待的过程中会休眠,但是I/O密集型还有大量的I/O访问,比如磁盘、网络等等。出于方便测试的目的,我们还是使用time.Sleep来模拟,主要测试goroutine在休眠一段时间后对性能的影响。

计算密集型的访问我们采用挖矿算法,通过计算hash值,满足一定的挖矿难度让CPU进行大量的计算动作。

测试分别采用并发连接数为 5000、2000、1000、500、200、100,测试对应的吞吐率和延迟。

测试使用多epoller的方式实现的服务器和goroutine-per-connection实现的服务器。因为连接数少,我们可以采用goroutine-per-connection的方式。

九、 I/O 密集型的服务器(无sleep)

首先测试I/O密集型的服务器,在没有sleep的情况下,两个服务器的数据对比如下:

多epoller服务器

代码: 10_io_intensive_epoll_server

5000 2000 1000 500 200 100
tps 210064 203027 207097 208460 200798 212587
latency(s) 23.2 9.1 4.5 2.3 0.9 0.5

吞吐率变化不大,基本都在误差以内,延迟随着连接数的降低而降低,基本成线性关系。
服务器可以达到20万的吞吐率。

goroutine-per-connection 服务器

代码: 11_io_intensive_goroutine

5000 2000 1000 500 200 100
tps 203038 208002 209128 207990 209192 212376
latency(s) 24 9.2 4.6 2.3 0.9 0.5

可以看到,当服务器的业务简单,基本没有耗时的情况下,这两种实现的差别不大,基本一样。

十、 I/O 密集型的服务器 (sleep 10 ms)

我们模拟I/O耗时10毫秒的情况,两个服务器的数据对比如下:

多epoller服务器

5000 2000 1000 500 200 100
tps 6218 6256 6251 6108 6027 4736
latency(s) 0.8 0.3 0.2 0.08 0.04 0.03

吞吐率急剧下降。

goroutine-per-connection 服务器

5000 2000 1000 500 200 100
tps 203088 194783 98895 49326 19747 9886
latency(s) 0.02 0.01 0.01 0.01 0.01 0.01

可以看懂吞吐率会和连接数相关,但是也不是线性关系,随着连接数的增加,所带来的吞吐率收益也慢慢的变弱,也就是有一个拐点,连接数的增加带来的吞吐率的增加将变得很小。

看它的延迟时间,连接数2000以下延迟就是都是业务所耗费的时间(10毫米)。

这给了我们一个启示,在连接数比较小的情况下,正统的goroutine-per-connection可以取得很好的延迟,并且为了提高吞吐率,我们可以适当增加连接数。

十一、 计算密集型服务器

采用挖矿算法,计算哈希值,如果哈希值的前12bit都是0的话算挖矿成功。

多epoller服务器

代码:12_cpu_intensive_epoll_server

5000 2000 1000 500 200 100
tps 212554 227291 224509 229796 226687 226147
latency(s) 0.02 0.01 0.004 0.002 0.001 0.0005

吞吐率基本不变,但是延迟随着连接数的降低而成线性降低。

goroutine-per-connection 服务器

代码:13_cpu_intensive_goroutine

5000 2000 1000 500 200 100
tps 211048 212343 228978 228756 228768 229425
latency(s) 0.02 0.01 0.005 0.002 0.001 0.0005

吞吐率和多epoller方式基本一致,延迟也一样。 可以看出对于计算密集型的服务,这两种方式的性能差别不大。