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

原文作者:smallnest

这一篇文章介绍了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方式基本一致,延迟也一样。 可以看出对于计算密集型的服务,这两种方式的性能差别不大。


版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。

本文分享自微信公众号 - Golang语言社区(Golangweb)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券