多线程服务器编程[3]-多线程服务器的使用场合和常用模型
本章研究对象:分布式计算的网络应用程序,基本功能可以被简单归纳为“收到数据,算一算,发出去”单线程服务器 最常用的为“non-blocking IO + IO multiplexing”,即Reactor模式,例如
- lighttpd
- Nginx
- libevent
- Java NIO
- Twisted(Python)
Reactor
【多线程服务器编程[3]-多线程服务器的使用场合和常用模型】结构
- 事件循环(Event-loop)
- 使用epoll进入阻塞监听事件,按照事件类型调用hanlder
- 需要非阻塞编程,回调函数必须是非阻塞的,只能在epoll处阻塞
- 这导致容易割裂业务逻辑,不容易理解与维护
- 适用于IO密集型
- 计算太多也会阻塞
- 虽然单线程模型不需要考虑同步问题,但不能很好的利用多核,而如果同时使用多个进程,则势必会涉及到同步问题
- 阻塞+每个请求新建一个线程
- 阻塞+线程池
- 非阻塞 + IO multiplexing 也即作者所称的“non-blocking IO + one loop per thread”
- Leader/Follower等高级模式
好处
- 线程数目固定
- 方便地在线程间调配负载
- IO事件发生的线程是固定的,一个连接会被注册到某个线程的loop中,并一直由这个线程负责,同一个TCP连接不用考虑并发
线程池
- 适用计算任务较多的情况,重要的基础设施是BlockingQueue实现的任务队列或生产者消费者队列
- 把计算任务或待计算数据分配给线程池中的线程
TCP的好处:跨主机,伸缩性,双向,无负作用,可记录,可重现,容易定位故障
多线程服务器的适用场合 处理并发连接的两种方式
- Go goroutine, python gevent等的语言提供的用户态线程,由语言的runtime自行调度
- 廉价,可以大量创建
- 通常配合“看起来是阻塞的IO”,这里指对于用户态的调度器阻塞,而不是对于操作系统阻塞,否则用户态多线程无法成立
- 内核级线程配合Reactor模式,例如libevent,muduo,Netty
- 数量与CPU数目相当
- 非阻塞IO
model分析
- 单进程+单线程 : 没有伸缩性
- 单进程+多线程
- 多进程+单线程
- 复制多份单线程的进程,使用多个TCP port提供服务
- 主进程+worker进程
- 多进程+多线程 : 聚集了2和3的缺点
- 需要fork的时候
- 需要限制程序CPU占用率的时候
- 单线程的Reactor的主要缺点是响应时间慢,多线程改善这一点
- IO吞吐(网络/磁盘)是瓶颈时,多线程不会增加吞吐,这时候单线程+单进程就够了
- CPU算力是瓶颈时,多线程不会增加吞吐,这时候单线程+多进程更合适也更简单
- 对比多进程
8核心,压缩100个文件
多进程:每个进程压缩1个文件
多线程:每个文件用8个线程并行压缩
总耗时相同,因为CPU都是满载的,但是后者能更快拿到第一个压缩完的文件,这体现了多线程的低延迟
但实际上,线程间算法的并行度很难达到100%,所以:同一个任务,多线程或许会比多进程吞吐量下降一点,但一定可以带来响应时间的提升。
- 能提高响应速度,让IO和计算重叠
- 相比进程每次切换都使CPU Cache失效,线程间切换成本小,因此适用于“工作集”比较大的情况
- 分割事务,将IO线程、计算线程和第三方库(例如logging)线程分开
- 计算操作:一个IO线程,8个计算线程(线程池),IO线程向BlockingQueue中添加数据,计算线程收到唤醒后开始计算
- 写操作:一个单独的logging线程,通过一个或多个BlockingQueue对外提供接口,别的线程把日志写入Queue中即可,不需要等待,这样降低了服务线程的响应时间
- 注意,读操作并不能利用多线程的便利性,因为无论如何都需要等到结果之后才能继续进行
- 当计算在每次响应请求中占比比较高时(20%以上)适合
- 能简化编程;避免实时创建线程的开销;减轻IO线程的负担(IO线程进行计算的话,就不能相应IO了,会导致响应速度变差)
- 线程池的大小需要阻抗匹配,例如8核CPU,每个计算任务线程的密集计算所占时间比重为50%,那么16个线程就能跑满全部CPU,线程池太小,会不能高效利用硬件,太大会导致额外的线程切换和内存开销
推荐阅读
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 爱就是希望你好好活着
- 昨夜小楼听风
- 知识
- 死结。
- 我从来不做坏事
- 烦恼和幸福
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- Linux下面如何查看tomcat已经使用多少线程
- 说得清,说不清