服务器维护socket连接池,socket连接池SocketPool分析(八):并发服务器

文章目录

并发服务器的历史线:

只启动一个线程,线程处理数据用阻塞的方式

这样的话会非常浪费服务器的资源。根本不叫做并发,同时只能服务一个客户端。充其量 CPU 每秒进行多次任务切换,task switching,任务切换就是并发的假象。

ac77e4e1ecfe2e5f8721ca37b26532c5.png

只启动一个线程,线程处理数据用非阻塞的方式

使用 fork 启动多个进程,进程处理数据使用非阻塞的方式

e7a79f2e5423271ed5bdd28e0ded9ae1.png

启动多个线程,线程处理数据用非阻塞的方法

但是线程的切换需要资源,如果对实时性有要求的话,不能容忍多线程切换。

创建子进程池,处理数据需要的子进程将从进程池当中取出

7c44e3669a57ec46dde860a75905f9ff.png

使用线程池来限制线程切换的数量,线程处理数据用无阻塞的方式。

但是线程池里,切换线程还是慢

使用 IO 多路复用,线程处理数据使用无阻塞的方式。(Reactor 模式)

通过使用epoll来实现 IO 多路复用。这里推荐使用 libevent。但是没有用到多线程,无法充分发挥多核 CPU 的优势。

f6af00bb7613a6fa00438759321ed053.png

reactor + worker thread(过渡方案)

用 reactor 来监听listenfd描述符,当accept()函数返回时候,然后启动线程,也就是为每一个连接创建一个线程,在线程中对 socket 进行读写。

reactor + thread pool(过渡方案)

先创建好线程池,用 reactor 来监听listenfd描述符,当accept()函数返回时候,然后从线程池当中取出线程,在线程当中对 socket 进行读写。

c8ffee6066576e755720f6a2836564cb.png

reactors in threads(one loop per thread)

对 socket 描述符的 read 和 write 也使用 reactor 模式

在 reactor + thread pool 当中,我们仅仅针对 listenfd 使用了 reactor 模式,而对accept()得到的描述符是直接使用阻塞的方式。而这里再加上让 reactor 模式监听 socket 描述符。意思就是说在主线程当中用 reactor 监听 listenfd 描述符,在副线程中用 reactor 监听accept()得到的描述符。

4c64e2ae001144ebf1d537a8fa1b129c.png

multiple reactors + thread pool(one loop per thread + thread pool)

启动一个线程池,分一半的线程给 reactor,另外一半的线程做其他计算。

4a9c27446e88f7f831450dadcc814c7f.png

使用 IO 多路复用,异步读写,线程处理数据使用无阻塞的方式。(Preactor 模式)

boost::asio 是 preactor 的库。但是它是使用epoll来模拟preactor模式的。

在我的文章《socket 连接池 SocketPool 分析 (三):《UNPv1》复习(下):IO 多路复用》中也提到过了。

使用epoll还是同步 IO,要使用异步 IO 的话,就要在 linux 下使用 AIO 系统调用了。请参看知乎上陈硕老师的回答:怎样理解阻塞非阻塞与同步异步的区别?

在 linux 下还是首选 reactor 模式。

f1dafcdb68de13b59e8c2ac0a171e185.png

常见问题:

linux 能同时启动多少线程?

答:对于 32-bit Linux,一个进程的地址空间是 4G,其中用户态能访问 3G 左右,而一个线程的默认栈 (stack) 大小是 8M,心算可知,一个进程大约最多能同时启动 350 个线程左右。

多线程能提高并发度吗?

答:如果指的是 “并发连接数”,不能。

假如单纯采用 thread per connection 的模型,那么并发连接数大约 350,这远远低于基于事件的单线程程序所能轻松达到的并发连接数(几千上万,甚至几万)。所谓 “基于事件”,指的是用 IO multiplexing event loop 的编程模型,又称 Reactor 模式。

多线程能提高吞吐量吗?

答:对于计算密集型服务,不能。

如果要在一个 8 核的机器上压缩 100 个 1G 的文本文件,每个 core 的处理能力为 200MB/s,那么 “每次起 8 个进程,一个进程压缩一个文件” 与 “只启动一个进程(8 个线程并发压缩一个文件)”,这两种方式总耗时相当,但是第二种方式能较快的拿到第一个压缩完的文件。

多线程如何让 I/O 和计算重叠

答:多线程程序如何让 I/O 和计算重叠,降低 latency(迟延)

例:日志(logging),多个线程写日志,由于文件操作比较慢,服务线程会等在 IO 上,让 CPU 空闲,增加响应时间。

解决办法:单独用一个 logging 线程负责写磁盘文件,通过 BlockingQueue 提供对外接口,别的线程要写日志的时候往队列一塞就行,这样服务线程的计算和 logging 线程的磁盘 IO 就可以重叠。

如果异步 I/O 成熟的话,可以用 protator 模式。

线程池大小的选择

如果池中执行任务时,密集计算所占时间比重为 P(0

假设 C=8,P=1.0,线程池的任务完全密集计算,只要 8 个活动线程就能让 CPU 饱和

假设 C=8,P=0.5,线程池的任务有一半是计算,一半是 IO,那么 T=16,也就是 16 个 “50% 繁忙的线程” 能让 8 个 CPU 忙个不停。

线程分类

I/O 线程(这里特指网络 I/O)

计算线程

第三方库所用线程,如 logging,又比如 database

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值