00:00
好,同学们,我们继续完成了bio,了解它是什么,对应的落地代码以及它的缺陷,那么自然而然,我们现在终于可以拍下从BIIO来到了我们NIO,在开始IO之前最后看一下这句话,每个线程我们上一步的痛点就是bio的痛点,我都要分配一个连接数,质量少的时候,并发量小的时候可以来一个利用一个,那么如果是多了以后,必然会产生很多个线程来处理,那么既然是多给缩,既然是多个so连接啊,假设现在啊,我们呢,有1万个所给的连接客户端连接连上来,我们必然要放入进一个容器,纳入统一管理,注意是容器,我可没有说是磁化技术啊,那思考一下这个容器该是什么?OK,什么spring容器啊?大哥,你怎么想的?OK,来吧,带着问题过来,先说no。
01:04
那么我们来多个了,现在的思路方式就是多个以前的思路,来一个溜一个,来一个溜一个。不好用,我们就想把它纳入统一管理。第二个,别给我阻塞,或者尽量给我少阻塞。好吧,我们来先看看这张图,那么弟兄们,哎,是不是交互变多了?来吧,是这样的,先读一遍,然后我再翻译成人话。当用户进程发出read操作的时候,如果内核中的数据还没有准备好,那么它并不会阻塞用户进程,而是立刻返回一个L。注意这个A不是说这个程序错过bug,你可以把它理解为。返回一个标识,就比如说我内核还没准备好。再等等,哎,只是告诉你,现在还没有办法给你反馈正确的数据,我在后台正在处理中。从用户进程的角度来讲,他发起一个读取操作以后,我要找你拿东西的话,我也不想等待,也不需要客户端等待,而是马上得到个结果。用户经常判断,如果是一个error的时候,他就知道还没有准备好,于是啊,他可以再次发送RA操作,一旦内核中的数据好了,并且又再次收到了用户进程中的system call,那么内核就马上将数据拷贝到用户内存当中,就是从内核的数据转给用户态,然后返回。所以NIO的特点是用户需要不断的主动的询问内核数据准备好了吗?一句话,用轮询替代阻色来,弟兄们。
02:45
还记得这个坑爹的模式吗?阻塞发起来杯奶茶,好等着,好等着后台处理完成。从头到尾组那。来吧,那么现在进程反复调用receive等待返回成功的指示,什么东东轮巡,所以非阻塞式IO就是IO,实质而言就是用轮巡替代了一直长时间的等待所有的轮巡,是不是有点类似于我们勾邮里面讲过的自旋那种思想,自选锁CS啊,OK,好了,那么他是这么干的,当用户发出。
03:28
点完奶茶以后,我奶茶好了吗?如果奶茶店数据还没准备好,那么它呢,并不会。跟客户说你别跑啊,你就给我在前面等着你的奶茶,我在给你坐着呢,不许跑,不可能这样对吧,他而而是呢。Linux内核就奶茶店的师傅呢,没有权利也不愿意去阻塞用户。而是立刻告诉用户说你的奶茶还在。制作过程当中,请待会儿来取,那么从用户的角度而言的话呢,我拿我的奶茶的时候,我也不想等待,对吧,我可以去商场上逛一下,那么。
04:09
我呢,马上就得到了一个结果,这个结果告诉我,要么已做好直接给我奶茶,要么说您的奶茶还在后台厨房处理中没做好,对吧,你这个时候用户判断结果还是一个A,我们相当于说制作准备中。他就知道我的奶茶还没做好,于是我去商场逛一会儿,比如说每隔十分钟我回来问一次做好了吗?那么一旦后台把奶茶做好了,并且再次收到了用户进行的system call。那么他马上。就把奶茶交到用户手上,所以NIO的特点是用户进程需要不断的主动询问内核数据,一句话用轮询替代阻塞,这就是NIOOK,大家请看,好了吗?没了好了吗?没了好了吗?没了也就是至少应用程序在这儿科普图,你可以中间有点空闲去做你的事,哎,完了以后数据准备好了,在这块再发回来,有没有发现第一段?
05:11
不用阻塞第二段没办法对吧,内核毕竟是要去处理的,所以将数据从内核复制到用户空间返回,OK,好,那么这个就是我们NIO的雏形。那么接下来我们先把这个面试回答拿下来,结合前面的bio开工,从NIO模式当中,一切都是非阻塞的。的方法,非阻塞,如果没有客户端连接,就返回没有连接的标识,OK啊,我也不想在这等着。明白了吗?直接告诉你,此时暂时没有人点餐,空闲中读取方法是非阻塞的,如果read方法读取不到数据,就返回空闲中的标识啊,对吧?如果读到了直接read的方法读数据的时间。
06:02
OK,那么在NIO模式当中,只有一个线程客户端与服务端进行连接了,这个so就会加入到一个什么当中,数组隔一段时间干嘛遍历一次,那么看这个搜瑞的方法能否读取到数据,那么这样一个线程就能够处理多个客户端的连接和读取了。所以同学们请看这。OK,多个连接过来好吧,那么对不起,一,我们现在用NIO,别给我阻塞,第二个我们在NL模式当中只有一个线程,相当于说那么多客户请求,哎呀,反正我在点单的师傅就在桌面上排了一大堆订单,好,现在就是这些下单的soet客户下单就会加到一个前台的数度当中,哎,就是他可能比如说这一口气出了十张单子啊,哪个奶茶做好了,隔一段时间好了吗?哎,我就通知人家来取,或者等他回来说,哎,你的好了好了好了,那么看这个速动read的方法是否能读到数据,读到了说明内核已经准备好了,那么这样一个线程就能处理多个客户端的连接和读取了,咱们也就不用不停的来一个拗一个,来一个拗一个,清楚啊好,我们同学们这就是从bio过渡到了我们NIO,好,同学们从这到这,理论说完,杨哥代码求证明,通过前面的讲。
07:29
我们都清楚这个bio,杨哥你自己写的啊。这些代码说难听点,它是new server socket,那么这个new server socket什么概念?也就是弟兄们,你先告诉我server socket,它是哪一代的JDK,那么同学们,我们不烦呢,随便点开一个bio啊。来。我们找到最上面几啊。
08:02
GDK1.0。就说这个索尔索塔。它是要阻塞的,它是会等待的,那么我们现在不想等待,那是不是要在这个serve数上面去修改啊?那么JDK1.0是什么意思?就是Java最原生出娘胎第一个版本多少程序构建在这个上面,如果你现在为了你个人的诉求,你就要去把这个最底层的源代码给我改了。那你告诉我会不会影响整体Java的健壮性和稳定性?那么下面我给你两个选择,第一个你艺高人胆大流氓,有文化牛逼,把JDK1.0的代码我再去优化,再去改了,那现在Java的除到JDK19了。那么请问你改1.0的版本会不会影响JDK19以后的代码,你要保证每一代都要通过验证。你觉得你有这个水平吗?好,这是第一个问题,第二个1.0的就别动了,祖传代码误动干嘛?
09:04
另外,开辟。一个战场另外做一套,既要有原来的功能,也要有新功能,不就完了吗?所以说,弟兄们来吧,上述以前的苏etit是会阻塞的,我们只好另外开发一套AI,这个时候弟兄们请看是不是叫java.io。听懂了吧?所以说在这儿噼里啪啦一大堆,那么a channel that can be multipled VI I,通过select选择器,我们可以获得我们需要哪个渠道,哪个频道相当于soet,所以呢,在这我们就会发现。以前录代码我们叫server socket,现在只好来一套新的,叫server socket channel OK,好,那么同学们来吧。从bio过渡到我们的IO,老规矩,IO nio1N2。
10:05
来还和刚才一样,那这个是一号客户端连接进来哪个IP哪个端口,我准备输入我们的数据,Quick离开,还是熟悉的配方,还是熟悉的味道,但是我们的NIO就不一样了,弟兄们这么看就不害怕了。来前面说过了,我们用bio的多线程版本之所以失败,就是因为来一个你一个,来一个溜一个,那么这个时候我们就要想,那么你来一个。欢迎,每次来就六一个受不了,所以我们需要把所有连上的的请求放到一个容器里面,这个容器就是我们的耳list,那么第二个我们分配的内存1024多少个字节,多少兆,这这个咱们1024个字节,咱们后面说啊,这会用得到那么一句话,现在这有个socket list,就是socket channel明白,那么IO这边是这么干的,弟兄们请看我呢。
11:04
先搞定一个容器,这个容器专门就装现在需要连上来的so的连接,也就是客服端,比如说目前几个二这个I size是不是就两个对吧?好,完了以后server启动等待,请看在这server so channel.open启动我们的server socket,然后绑定哪个地址IP和我们的端口号,在这块它可以配置设置为什么非阻塞模式,啥意思呀?弟兄们请看一下我们的serve所给的channel几J1.4以后,也就说这货既有包含老功能的。也包含自己新功能的,那么你这儿可以非常灵活的设置阻塞还是不阻塞,也许有时候你要用到阻塞啊,那么在这儿它的意思就说什么,就像是一部手机双卡,你插联通移动的都可以,所以在这儿跟前面那个差不多,我申请了一个容器分配的1024字节B法,这个是一些常量定义,好说你们以后缩都放放这我们的服务器就它真真正正是在这儿来要求同学们楼沿,我要错,和刚才一样,我这是不是开始监听了。
12:18
来吧,这个熟悉吧,但是注意我这配置是什么,非阻塞accept不阻塞,往下看它这样题,首先我便利这个list刚刚开始,我只启动了我们的服务器,一号客服,二号库客服有没有连上来没有,所以在这。他就会去读,也就是说每次读1024个字节这样的,对吧,小口小口的吃,小口小口的读,读得到,那么呢,我们呢,就说明我们这个so连接里面一定是由某一个客服端,它有数据了,那么读得到肯定会大于零,读不到,那是不是就代表负一,负一的话,咱们呢,就不多啰嗦了,那么好在这停不停这一秒都可以啊,那么来也就说假设是负一,负一大于零吗,负四,所以说跳出这个循环往下走,否则的话也记。
13:15
现在是有两个,哪怕他有100个,我就遍利这个I list,这个soet list,谁有数据我打出来,没有数据不打扰,OK,把所有的客户端请求都装进这个socket channel这个容器里面,那么接下来他怎么干呢?来每个server socket。在这就监听接收着我们的请求,如果不等于,那就说明什么,有人连上来了,只要一连上来还是继续设置为非阻塞,只要一连上来,就会把我们的这个一号顾客装到list里面,二号顾客装到list里面,这一步就是往so serve soet list里面添连接的一个容器,OK,那么然后在这个容器里面我们呢,就会发现有多个。
14:11
连接,那么接下来就是谁有数据我打出谁,那么这样是不是就搞定了我们之前所说的来一个分配一个,来一个分配一个,没有一个线程搞定,所有的连接都装进我们的容器里面,那么接下来便利这个容器谁有数据我打出来,OK,好,那么同学们演示一下我们的效果。启动一下我们的程序这块是不是IO的,这个服务器启动中进来了吧,咱们来弟兄们,下面呢,我也没有什么,就静静的等着,好SERVE1。一号顾客启动red k0一来吧,Start,来,我这也就准备要输东西了,那么还是和以前一样,是不是叫一号顾客一次?好吧,那么来同学们请看,成功连接了这个server,这个soet list里面几个size是一,就是已经有一个连接装到这个容器里面了。来现在一号顾客一次,弟兄们,一号顾客两次。
15:22
蝼,现在是不是读取数据十次什么意思?来再来看啊,ABC多少三就字节的意思听懂了吧,哎,这个就是它的一个长度,我自己打出来的,那么下面的意思就说一号顾客一次二次ABC乘没乘高完全正确,没有什么来一个分配一个,来一个分配一个,那么来现在我二号顾客也。登堂入室了,下面回到我们NIO,大家请看现在是不是成功连接,成功连接现在server socket list里面几个有两个客服,也就是有两个连接装到这个容器里面了,那么和刚才的一样,这个时候是不是就变成了?
16:14
嗯,他写不了这个去,那么二号顾客依次来,弟兄们请看二号顾客一次互不影响,互不打扰吧,那么一号顾客又发功了,那么现在三次,那么来弟兄们,二号顾客二次手表端怎么着?一号顾客我们来看三次,二号顾客二次,前后相关,互不打扰,那么所以说弟兄们这样的话,是不是用轮询就替代了我们对应的阻塞呀。第二种方法好过第一种方法,不用你每次连进来一个客户端都要去new一个socket,干嘛要利用一个线程来处理这个socket,通通装进容器给你搞定。OK,这个就是我们扣解决落地对应的案例。下面请同学们再思考当前这个模式又存在哪些问题呢?否则我们干嘛要爬到我们的NIO下面的下一代IO多路复用呢?思考一下。
17:26
针对于我们刚才完成任务的代码,咱们呢来总结一下当前IO模式所存在的问题和优缺点。首先NIO成功的解决了bio需要开启多线程的问题,不再需要哪一个索ET套接子连接内部叫六一个线程去一一匹配和处理,也即NIO当中我们一个线程就能解决多个socket连接,将多个soet连接存放于同一个容器里面去循环,便利用轮巡替代的阻塞即可。但是它有两个问题。
18:06
第一个这个模型是在客户端少的时候十分好用,但是客户端如果很多呢?1万个连接,我每次变列就要变列1万个socket,就是他架不住,就是这个还是这个socket多了,反正一个现在的soet统一的放到这个soet list这个容器里面那1万个。爽死你了,我这儿就要便利1万次,而且肯定有一个问题是我便利那么多次啊,我这有一个if是什么概念?就是要有数据我才处理,没数据的我也要去便列,就是假设1万个线程请求你1万个缩的连接里面只有两个有数据,那么我剩下的那9998个完全是做无用功,完全是陪跑的。OK,所以呢,请看一眼,如果1万个so当中,放在这个容器里面的这1万个so连接当中,只有十个so有数据,它也要便利1万个索给塔。
19:12
那么这个时候就会做很多无用功。每次便利遇到负一。仍然就就会返回是我没做什么,但是仍然会浪费一次系统资源的调度。第二个。这个便利过程是在什么态用户态进行的,那么用户态判断soet是否有数据,还是要调用内核的RA方法去实现,这就涉及到了用户态和内核态的多次转换,这么说弟兄们能不能理解,也就是它底子这些server还是去掉底层的那些东西,那么这样的话呢?我们呢,每便利一次啊,我就要切换一次,开销很大,这些问题呢,就是他们存在的隐患,优点NIO不会阻塞,在内核的等待数据过程当中,每次发起IO请求可以立即返回,不用阻塞等待,实时性较好。
20:12
来吧,我这便利了。以后只要这。不为难,我加进去。为难那就跟无视了,就是轮巡多了以后又会产生另外一些问题。那么来。缺点轮询将会不断的询问内核搞没搞定?我的奶茶好了吗?我的奶茶好了吗?这样会占用大量的CPU时间,资源的利用率比较低,所以一般的外外部服务器呢,不使用这种。IO模型,那么来结论,让林内X内核搞定上述需求,这也就是为什么我们要从NIO跑到IO。多鲁夫用意思就是说,我现在要保证这些优点,屏蔽客服这些缺点那。
21:03
这个便利过程是在什么用户态进行的,就是说你不管怎么说,这段代码是不是周扬写的?没错。那么有一种可能,妈的,干脆要做这些事的话,我们人别写了Linux系统,要不你牛逼一些,你把这段代码塞到你的内核里面给我,把用户态和内核态的这个切换过程给我省略,我只需要你直接给我就完了。讲完了,这就是为什么我们会进入到艾欧多罗浮右,所以请看我们的结论,让Linux内核搞定上述需求。我们将一批文件描述符,你可以把它理解为就是什么那些你一大堆连接soetit通过一次系统调用传给内核,用内核存序变列,内核是不是肯定要比我快才能真正解决这些问题?哎,多的复用就会应运而生,也即将上述工作直接放进Linux内核,不在用户态和内核态转换,而是直接从内核中获取结果,因为内核是非阻塞的,这个就是我们IO多路复用这种思想产生的真正意义,这也是为什么我们到最后我们手工编写的这些代码。
22:20
变成了我们后面要介绍的什么。Select和一函数,因为他把我们能想到的这些东西干脆直接说,哎,你们也别在这思考了,我直接给你们做完,我内核里面天天带着,你直接复用我的什么一函数就可以了。听懂这个就是我们出现的原因,所以大家就会明白怎么才从bio到NIO再过渡到IO多了夫幼。
23:00
明白了它的演化顺序,那各位同学,接下来老规矩,我们是不是要来进行一下非阻塞式IO的总结?来,我们下面所谓的问题升级就是从NIO升到IO,多罗妇幼一句话,我们如何用单线程来处理大量的连接,那这个时候是不是我们就用了一个线程,OK,放到这个容器里面轮巡,那么就把这段塞到我们的内核里面好,所以呢,结合前面啊,我们的非阻塞是IO,情况是这样的。客户端不等允许检查一下,有数据了吗?你的奶茶制作准备中,请稍后再试好,过了一分钟又来问好了吗?准备着稍后再试好,我回去该瞒瞒我的,那么检查检查检查这一段就非阻塞,但是对不起。
24:00
我客户端买我的内核。满你的,但最后啊,将数据从内核复制到用户空间,这段省不了,所以这有点小阻塞,对吧,你只需要承接从我们的内核态到用户态,你得把奶茶从厨房送到客户手上嘛,做这段完成,所以呢,在这块就是我们非阻塞式IO相关的介绍。
我来说两句