如何提高吞吐量
快期末考试了,作为学霸的你(对,就是你!)忙的春光满面。
一大波学妹来让你辅导。
你把她们聚集到了大教室里,对她们说:一个一个来。很快一个妹子过来了,但却得等她翻书,找题,整理思路,甚至还要先照照镜子问你她漂亮吗,最后才慢吞吞的开始问你题。你眼看着后面排着长队的妹子在等着你,但你却只能如此缓慢的一个一个亲(禽)手(兽)辅导,效率真低。一天才能接触几个?
这就是
单进程单线程阻塞模式
。 如果遇到哪个妹子缠着你一天,你就别想接触其他妹子了。
改进一号
于是你想这么不行啊,妹子们需要你。于是你学会了分身术,把自己分成N个,同时给N个妹子服务,于是把妹效率提高了N倍。当你还在高兴的时候,问题出来了。
教室就这么大,不可能把自己分身出太多,否则一个教室全是自己的分身,妹子进不来,搞毛啊。 不行,一定要限制分身的数量。此时你内心极度惆怅,唉,难道把妹效率有个不可逾越的鸿沟吗?
尼玛,人可以分身,但水杯只有一个啊。你的分身老是轮流站着你的水杯,你早已饥渴难耐,却拿不到水杯。于是你规定,谁拿了水杯,就在黑板上标记一下。用完了,要及时把标记擦掉。这样其他分身如果看到黑板有标记,那就等等,当发现黑板没有标记的时候,你们就去抢,谁先抢到,他就去做标记,独享水杯。
但后来一些各种蛋疼原因,黑板上的标记没有被清除,于是你和你的分身们渴的实在没法辅导,于是把妹效率降低很多。
这就是
多进程/线程模式
。 目前Linux很多经典的服务都是这种模型。但缺点也显而易见:
多进程/线程的数量在 一台机器 上数量是严重收限制的。
早期一些服务是动态创建/销毁进程。在当时看起来很不错。链接多了,就多来点进程给那些大爷们服务。于是在大量链接的冲击下,服务不停的创建新进程,最终导致系统资源枯竭,服务器失去响应。(现在的Linux会直接杀死占用内存过多的程序,从而保证系统自身处于可响应状态)
线程虽然轻量一点,但在Linux系统中,线程会占用8M栈空间,对于要保持长链接的请求,也不可能弄多少线程出来。
来,让我们和锁一起愉快的玩耍 不说了,你们先玩,我走了
关于多线程还有
leader-follow
,Half-Sync/Half-Async
等模式。它们对多线程做了很多优化,可以处理大量链接。但我并不了解,所以这里也不做陈述。
改进二号
恩,你想着要改进,于是先不分身了。
你有了新办法: 你首先请了你的死党,他就站在教室里。
然后你对妹子们说,谁准备好了要我来辅导,就先给我说一下,然后就准备着,等准备好了去给我兄弟说。我知道了就会叫你。
有妹子来到你这儿报名,然后她们就下去准备问题了。你就把她们的名字记在一张纸上,当你空闲的时候,你就把纸给你兄弟说,看看这上面谁准备好了,然后你兄弟告诉你谁好了,你就把那个妹子叫过来,搞定后,再次把名单给你兄弟,不停的这个循环。
但后来又发现问题了。
纸太小,来报名的妹子太多,记录不下。
-
你在空闲的时候得把整张纸给你兄弟,你兄弟要依次看一边名字,然后找出谁准备好了。最后,还要把纸给你传回去。
你兄弟要抱怨了,尼玛老子不停的看这密密麻麻的名字,好累啊。跟你传纸的频率比老子lu的频率都高啊!
这是
基于事件的select
模式。(或者叫多路IO复用)
它的限制很多:
你需要把要监听的fd加入一个列表中,Linux系统默认列表大小只有1024
每次你都需要把这个fd列表传递给select调用。select调用再返回给你哪个fd上你感兴趣的事件触发了。也就是每次都需要从用户态->内核态->用户态这样拷贝一次。系统消耗太大
改进三号
恩,select方式要不停的轮寻,限制多多,效率也没高哪去。
于是你又改进了一下方式:
妹子还是到你这儿来报道,但你不用纸记录她的名字,而是立即告诉你兄弟,谁谁谁报名了,你留意下,她要是准备好了,你把她名字告诉我。
然后当你没事干的时候,就可以睡睡觉什么的。只要等着你兄弟来叫你就行了。
这就是
epoll/kqueue/ICOP 事件回调
机制.
epoll 对应Linux
kqueue对应BSD
ICOP对应Windows不用 select 那样的去轮寻,而是注册fd和回调,等待事件通知即可。
libevent
,libev
这些库就是将系统提供的事件回调机制的封装,供上层应用程序方便使用。
协程
当然,除了上面那些,还有一种模型,被称为协程。
特点如下:
- 由程序自己产生,管理,调度,销毁。和OS没一点关系
- 在一个OS进程中,可以产生百万级别的协程。
- 基于协程的Actor模型不共享数据,写并发程序得心应手
目前支持协程的语言有
这里也得提一下 Scala,它也有Actor模型,而且akka库也很强大。但应该不属于协程范围。
Python 是通过 Gevent 库来实现的协程。
我在工作和自己的折腾中都用过Erlang和Gevent,从协程角度讲,它们都很好用。
- Erlang 在各个process之间发送消息,不共享。没有锁的烦恼
- Gevent可以让你用同步的方式来写异步程序
看起来协程好处多多,那么 它有什么缺点呢?
- 因为协程还是运行在一个OS进程中,所以协程不能跑阻塞任务,否则就要将整个OS进程阻塞住了。
后面再写个文章具体讲讲我对Erlang和Gevent的看法