并发模式
继上一节最后的事件模式之后, 我们稍微提一下两种并发模式
半同步/半异步并发模式
简单地说: 同步线程处理业务逻辑, 异步线程处理IO事件. 异步线程将请求插入请求队列.同步线程获取请求并处理即可, 这与我们平时所使用的范式是一致的
而半同步/半异步只是并发模式, 我们真正使用的基本是变体 -> 半同步/半反应堆模式, 即:
- 异步线程只有一个, 主线程进行IO事件的控制
- 同步线程有多个, 即为线程池. 从请求队列中获取事件进行处理
- 如果插入队列的是socket, 即为
Reactor
, 如果是完成后的请求, 即为Proactor
缺点就是:
- 共享队列,很明显的需要加锁.耗费CPU时间.
- 工作线程同时只能处理一个请求, 请求过多时, 会明显造成延迟
那么, 有什么解决办法呢? 就是使用主从Reactor模式, (擦, 垃圾<高性能服务端编程>, 绕来绕去, 半同步/半异步 就是Reactor模式的`单Reator, 多线程配置`)高性能服务端编程>
领导者/追随者模式
相比于上一种并发模式需要的加锁保护. 主从模式(leader/follower
)就是为了取消锁机制的 简单地说: 此模式是各个线程之间轮换处理IO事件的一种模式, 领导者进行IO监听, 追随者或者休眠, 或者执行任务
每个领导者, 首先从线程池中推选出新的领导者, 然后进行IO事件的监听
包括的组件: 句柄集
, 线程集
, 事件处理器集
, 具体的事件处理器集
缺点也比较明显: 只有一个句柄集, 不能像Reactor模式一样, 在多个线程中进行各自的监听连接 PS: 主从模式,实在用的少, 也没见过比较成熟的项目, 暂且搁浅了.
解决C10K问题
(解决方案, 参考自C10K问题的源网页 website»)
既然要说解决C10K问题, 我们就先考虑从哪些方面着手:
- 网络库
- IO策略
- 一些其他手法
网络库
当我们使用网络库的时候, 就不需要考虑这些问题了. (曲线救国, 变相解决) 如: ACE
, Boost.Asio
, libev
等当然还有其他语言的诸多框架
IO策略
这是我们进行问题解决的直接有效办法, 有下面这几种策略:
- 一个线程服务多个连接, 使用LT模式
- 一个线程服务多个连接, 使用ET模式
- 一个线程服务多个连接, 使用异步IO
- 一个线程服务一个客户端
- 将服务器代码构建进入内核空间
事实证明, 前三种方法是可行的. 前两种都是Reactor
模式, 第三种是Proactor
模式 至于第四种, 已经不可用, 一台机器要达到TPC, 去满足C10K, 根本不可能, 资源限制的问题 第五种, 基本没有成熟的项目, khttpd
也仅仅是服务静态网页, 局限性很强, 构建多了, 也会造成内核膨胀
其他的一些方法
- 1.将TCP堆栈带入用户空间 TCP堆栈中会发生很多的拷贝, 带入用户空间能够明显提升效率, 而且一些不必要的机制可以去除
Sina
就曾开源过fastsocket
就是一个类似的产品, (不过后来销声匿迹了, 代码在github
上有) - 2.解除打开文件句柄的限制 这个”解除”并非是真正意义上的解除, 肯定不可能无限打开. 我们的意思是: 释放到合理可用的范围内 一般有两个方面: 全局限制, 进程限制. 全局限制修改
/proc/core/fs/...
, 进程限制修改ulimit -n
- 3.零拷贝 我们之前介绍过一批函数
sendfile(2)
,splice(2)
,tee(2)
, 所谓零拷贝是较少不必要的拷贝, 不可能做到真正的零拷贝 - 4.避免小帧 避免小的帧,尽量达到一个
MSS
再发送, 但是, 如果是为了支持连接可以这么做. 使用writev
等 但是, 我们一般进行服务端开发, 是要禁止Negle算法
和ACK延滞算法
的,为了响应效率,小帧在所不惜
上面这些基本上就是解决C10K问题的办法了.
现在面临C10K, 已经没有多少挑战难度了, C100K也行, C1000K扣扣剪剪也行, C10M才是真正的挑战
展望未来: 可能更多的解法在于:
- 硬件,
- OS本质上的问题(内核空间的切换开始开销),
- 调度单位的重量(引入,
corutine
)
同时, 单机的性能已经趋于极致, 我们更多的研究关注点, 应该放在分布式的开发上咯
PS: Emmmm, 我复习网络编程的内容差不多就这写了, 以后机会了也可以去分析一下知名网络库的源码, 如Boost.Asio
, 同时要了解的还有TCP/IP
的相关内容, 继续复习去咯