在学习IO模型的时候,由于没有实践经验,很难理解这些IO模型有什么区别。后来偶然的机会看到一篇博客使用开饭店的故事来理解IO。今天,我就仿照他的写法来解释一下什么是BIO、NIO、AIO。有些地方为了符合IO模型,可能会有些不合常理,大家姑且看看。
1 故事背景
小叶毕业后就到某厂干程序员,从23岁一直干到28岁。虽然现在干程序员压力也没那么大,但是经不住父母催他回乡娶媳妇。好在干了五年程序员,他也攒了一些钱,他打算回家先开一家火锅店,也能聊以谋生。
2 一阶——麻辣串串——BIO
刚开始的时候,小叶也没有餐饮业的经验,只凭着一腔热血和自己对火锅的喜爱就开始了,因此他也不敢投资太多,打算先开个串串店练练手,店名就叫“一叶串串”。
这时,整个“一叶串串”只有小叶一个人。每来一个客人,他就会问客人需要什么,然后将客人点的串串煮好,再端给客人。服务完一个客人后,再去服务下一个客人。
生意虽然还不错,但每天从早忙到晚,小叶实在顶不住了,他觉得这比干程序员还累,每天关上店门只想倒头就睡。而且,随着小叶的手艺越来越好,客人也越来越多,经常有客人等太久而走掉的情况,他觉得非常可惜。
思前想后,他决定招几个兼职,反正隔壁的王大妈、张大婶、谢大娘闲着也是闲着,还老爱给他张罗对象,不如叫她们过来帮忙,给她们算工钱。说干就干,他联系好了几个天天在门口跳广场舞的大妈,如果有需要,就找她们来帮忙。
小王深信马老师“未来的制造业一定是服务业”的理念,并将这个理念贯彻在“一叶串串”的经营模式中,每一个客人从进店起,都要有一个服务员定向服务,直到客人离开。来一个客人的时候,他就叫王大妈,再来他就叫张大婶,再来他就叫谢大娘。客人走后,她们就回去该唠嗑唠嗑,该跳舞跳舞。这样做后,小叶的压力果然减轻了许多,每天的营业额也渐渐多了起来,看着账户里的余额,笑到合不拢嘴。
这就是我们说的BIO模型,映射到BIO中,上述各角色分别为:
- 每个客人对应一个客户端请求。
- 小叶对应Acceptor线程,他只负责监听客户端请求,并启动其他线程来处理请求。
- 王大妈、张大婶、谢大娘每人对应一个线程。
BIO就是:由一个独立的Acceptor线程来监听客户端的请求,每当有一个请求发生,就启动一个新线程去服务这个请求。当请求结束时,就销毁这个线程。
3 二阶——麻辣火锅+串串——伪异步IO
这样的日子持续一段时间后,小叶的烦恼又来了:虽然每天都有钱赚,但是他算了一笔账,即使一直这样持续下去,到他攒够钱买房子娶媳妇,也还要好几十年。老娘催婚的脚步步步紧逼,就差一哭二闹三上吊了。但是急也没有办法呀,这个小庙就这么大,香火就这么多,还能翻了天吗。
最后,还是一个客人的话给他提了醒。这个客人在吃完买单的时候跟他说:“你们店的串串真好吃,比河底捞的也差不了多少。”真是一语惊醒梦中人,为什么不扩大规模呢?河底捞能开这么多家连锁店,我一叶也行。想到这里,小叶的心思又动了起来。他看到旁边的几家店生意都不怎么好,都张罗着转让,索性就把他们都盘了下来。重新装修,开了一家三百平米的店,除了串串之外,还经营起了火锅,店名就叫“一叶火锅”。
扩建之后,小叶又发愁了,原来的经营模式根本行不通。一是客人太多,根本没那么多服务员给他叫。二是叫服务员也要花很多时间。在没有足够的服务员时,他只好让剩下的顾客先回家,下次再来。
这就是BIO的弊端:
- 线程是很宝贵的资源,在高并发情况下,无法提供足够的线程去处理请求。
- 创建和销毁线程会带来大量的开销。
于是,他只好加工资,把王大妈他们都聘请为全职服务员,仍然贯彻原来的服务理念,一个服务员跟一个客人,当人手不足的时候,再叫学生兼职来。他还是负责调度,每当有一个客人来,他就叫一个服务员去服务这个客人。如果服务员不够了,就先让顾客拿号排队。碰到节假日,队伍太长,就请几个兼职过来。
这就叫伪异步IO,映射到伪异步IO中,上述角色分别为:
- 每个客人对应一个客户端请求。
- 小叶对应Acceptor线程,他只负责监听客户端请求,并将请求转交给线程池执行。
- 所有服务员组成了线程池,全职服务员是核心线程,兼职服务员是除核心线程外额外开启的线程,没位置的顾客排期的队伍就是线程池中的等待队列。
伪异步IO就是:使用线程池来管理IO线程,当有新的客户端接入时,将客户端的 Socket 封装成一个Task投递到后端的线程池中进行处理,由于线程池的等待队列机制,可以避免多少个客户端并发访问时,资源耗尽导致宕机的情况。
为什么叫伪异步IO呢,因为在小叶看来,每来一个客人,他只要将客人扔给服务员,然后等着收银就可以了,这看起来任务就像是异步执行的。实际上,在线程池内部,仍然是每一个线程服务一个客户端,是同步阻塞执行的,只不过省去了开启线程和销毁线程的时间。
经过了这样的改变后,“一叶火锅”又可以经营的红红火火,日进斗金了。小叶每天看着账户里的余额,笑到合不拢嘴。
4 三阶——火锅城——NIO
经过若干年的经营,小叶也算小有成就,“一叶火锅”在家乡这个小县城也算是无人不知,而他也成为了一个小老板。但是他并不满足于此,恰逢这两年县里大力发展旅游业,外来游客渐渐多了起来,每天店里的客人都能赶上两年前的节假日了。小叶仿佛又看到了商机,有了上次扩张的经验,小叶这次准备大张旗鼓的干了。他打算钱一筹够,就将三层的店面都租下来,直接开一家火锅城,到时候让“一叶火锅城”成为县里的标志性火锅店,那岂不是当上大老板,赢取白富美,走向人生巅峰。想着想着,他就合不拢嘴了。
不过小叶似乎高兴的为时过早。他不仅把之前赚的钱都投进去了,还贷了一大笔款。现在火锅城终于开起来了,但是资金回笼的也太慢了,每天的利润,还不够填银行的利息。小叶知道,虽然开业的时候银行的行长都来给他道贺了,但是一但他不能按时还上利息,人家肯定要翻脸不认人的。到时候一旦破产,非但十年辛苦付诸东流,甚至人还要锒铛入狱。想到这些,他是饭也吃不香,觉也睡不好。
为了贯彻他的服务理念,他又请了一大批服务员,仍然采用一对一的服务模式,顾客们也是对此赞不绝口。但是要养活这么多服务员,每个月光工资就去掉了营业额的一大半。并且,有些客人,一吃就是三四个小时,这样一个服务员就要一直站在旁边服务他。而还有一些客人,是他曾经的竞争对手,进来啥也不点,就点一杯白开水,一坐就是一天,也要占用一个服务员。常常导致有空桌子但是无人服务的情况。小叶可愁坏了,到底怎么做才能既不降低服务质量,又能够节省成本,还不让这些客人占用服务员呢?
这就是伪异步IO模型典型的弊端,虽然相比BIO,它能够通过线程池,等待队列等方法避免多并发请求直接返回失败。但它本质上还是阻塞IO模型,当大量线程被阻塞时,会严重降低服务的效率,甚至一个空的连接不做任何其他事情也会占用一个线程。
在又一个无眠的夜晚,小叶对月独酌,迷迷糊糊想起了曾经当程序员的日子。如果当初不辞职,现在的我会是什么样呢?是成为一代技术大牛,还是惨遭裁员,穷困潦倒。他又想起了他当初在A厂当实习生的时候,师傅手把手教他写代码的日子。“你这里要用NIO,用BIO的话高并发下是顶不住的,我来教你改吧。”师傅的话语仿佛还在他的耳畔,他的手感到一阵温热,好像是师傅的手按在了他拿鼠标的手上。原来,他早已泪流满面,打湿了手背。突然,他好像想到了什么,他一个激灵,抓住了这一瞬间的灵感。NIO,就是NIO,谢谢你,师傅,没想到在分别了这么多年,还能得到你的教诲。想到这里,他的泪水又止不住的流下来。
第二天,小叶一早就召开紧急会议,宣布裁掉一部分服务员,给予他们法定的补贴,并宣布了新的经营模式。首先,将每一顾客从进店到离开的过程分为若干个阶段,接待、找桌、点菜、上菜、其他服务、结账、打扫等。在每桌设立每种服务的牌子,当客人需要某个服务的时候就立起对应牌子。然后,小叶自己负责巡视,查看各个桌子是否立起牌子,如果有,就安排一个服务员来服务。最后,其他所有服务员待命,一旦接到任务,就去服务,任务完成后,就回原地继续待命。
这就是大名鼎鼎的NIO模型了,映射关系为:
- 每个桌子对应一个channel
- 小叶对应selector
- 所有服务员对应线程池
- 服务顾客的过程对应网络连接的请求、解码、处理、编码、回复等阶段。
- 桌子上的牌子就是socket的状态
NIO简单来说就是:通过多个channel来表示多个请求,使用selector来轮询各个channel的状态,然后对每种不同的状态调用对应线程去处理。
使用了NIO模型后,每个服务员可以服务多桌客人,降低了成本,客人也能及时得到周到的服务,保证了服务质量,所有的桌子也都能安排客人,增加了客流量。 简直一举多得,小叶看着账户里的余额越来越多,又笑得合不拢嘴了。
5 四阶——信息化火锅城——AIO
解决了这个问题后,小叶心里对他曾经的师傅非常感激,如果不是想起了他曾经的话,可能自己已经身陷囹圄了。于是他拨出了那个一直记在心里却又不敢打的号码:
”喂,我是小叶啊。你不记得我了吧,我原来在A厂实习过呢,那时候你是我的师傅。“
”我怎么会不记得你,这么多年,你的号码也没换过,我还存着呢。“
”啊,哈哈。我打电话过来是为了感谢你的。最近我的火锅城经营出了点问题,要不是想起你当初教我的NIO模型,我可能就破产了。“
”哦,是吗?其实我也没教你什么,都是你自己好学,天天来问。具体什么情况,跟我说说。“
”是这样的……“
”哦,原来如此,那师傅可要再教你一招了。我问你,是不是有些客人有选择困难症,点个菜要点一个小时,服务员还得一直在旁边等着?“
”是啊,你怎么知道?“
”我再问你,是不是有的客人买单的时候,又要核对菜谱,又要开具发票,这些都需要服务员服务。“
”对啊,你说的可太准了,还有许多这样的事情,占用了服务员大量的时间,快教教我怎么解决。“
”我告诉你,还有一种IO模型叫AIO,是真正的异步IO。你只需要请人开发一个小程序,然后在每桌贴上二维码。客人需要点菜的时候,只要扫这个二维码,自助点菜,你只需要关注成功下单信息。结账是同样的道理,客人可以扫码自己核对订单,填写发票信息,然后你收到付款通知后直接开具发票就可以了。本质上,你是将所有任务分成两类,一类是马上就可完成的任务,比如上菜、拿毛巾等,这类任务你马上安排人完成。另一类是需要等待操作完成后你才能继续你的任务,比如点菜、结账,这类任务你就扔给他一个自助的方案,并告诉他通知你的方法,你收到通知再继续。这就是AIO模型。“
”秒啊,我怎么没想到,真的太厉害了。“
”哪有,这也是多年摸爬滚打的经验。“
”对了,这几年你过得怎么样?“
”我啊,就那样呗……“
……
此处省略一万字。
在这里此处总结一下AIO与故事中的映射关系:
- 服务员对应线程
- 顾客对应网络请求
- 第一类任务,不需要等待IO的业务逻辑代码,在服务端快速执行。
- 第二类任务,需要等待IO的业务逻辑,IO交给操作系统完成,并给操作系统一个回调函数,系统完成后回调方法,再继续执行业务逻辑。
AIO的核心就是:将IO操作交给操作系统来执行,并传递回调函数给操作系统,这时候应用程序可以去做其他事情,当IO完成时,操作系统调用回调函数,然后该业务逻辑可以继续执行。
6 参考
https://blog.csdn.net/szxiaohe/article/details/81542605