00:02
Ninety模型里面的任务队列我们来看一下,同学们想一想,在前面我们讲ninety模型的时候,我们说到在IO event loop里边除了有select,还有一个特别重要的对象就是task q。任务队列。呃,我们知道,就是在整个事件循环的过程中,我们会去处理,就是在这个拍烂,我们会有一系列的handle,对我们的业务或者对我们的数据进行一个处理,那问题就来了,如果说我们在某个handler里边会有一个长时间的操作。长时间的操作,那么势必会造成我们这个拍烂呢,会有一定的阻塞,因此呢,对于某些任务,我们可以把这些任务提交到他对应的task q里面去异步执行。对亦不执行,也就是说他不会在这里主赛道,他该他在处理某一个事情的时候呢,在这个handle里面去处理一个长时间要去,呃,就是花费时间比较长的业务的时候呢,它可以进行异步处理,怎么异步处理呢?来我们来看一下,就说怎么把我们的一些。
01:17
花费时间比较长的操作,提交到我们这个event n I event loop里面的task q里面让他执行。那我再多说一句,这个他Q呢,其实它是跟我们这一个channel是有一个绑定关系的,我们来看一下。回到这边来,我们看。在。呃,任务队列里面,也就是我们NIO里面的loop,它的任务队列里面呢,这个task有三种典型的应用场景,第一种呢,就是用户程序自定义普通任务,就说我们可以自定义普通任务,把这个任务提交到我们这个任务队里面队列里面去,这是第一种。第二种呢,用户自定义定时任务,就说我们也可以通过这个schedule在schedule,然后呢去指定一个定时的任务。
02:06
还有一种方法呢,就是非当前reactor线程调用China的沟通方法,这种方法呢,呃,它的应用场景是,比如说我们在推送系统的业务线程中,根据用户的标识找到对应的channel,待会儿我会讲这个流程是怎么做的啊,就是我们可以因为你每个用户会对应一个channel,我们可以通过一个集合来管理这些channel,通过用户标识呢,找到某个用户的channel,然后调用right类方法向该用户推送消息,这是非常重要的一个功能。就会进入到该场景,最终right会提交到任务队列中被异步消费,那这里面呢,它有几个核心的关键点,第一个就是推送业务。推送系统对不对,我们经常会在网络编程里面会用到这个推推送业务,然后呢,根据用户的标识。就是你每一个用户就客户端嘛,这个用户就所谓的客户端,每个客户端呢,我可以给你一个标识,我可以设置一个标识,然后通过这个标识呢,找到它的China引用。
03:08
那怎么去找到呢?显然我在我在我的服务器里边可以去维护所有的channel,根据用一个集合就可以搞定嘛,对我我用这个用户的标识对应channel就可以了,哎,然后就是在在干什么呢?调用right类的方法给该用户推送消息,最后呢,会被异步的消费。好的同学们,那现在呢,我们来给他举个例子,我们先来看第一个案例,就这个,第三个案例呢,到时候我只说一个思想就行了,好吧,并不难啊,待会我会说具体大家怎么去做并不难,只要把第一个第二个搞懂了,那第三个自然不在话下。我看第一个用户程序自定义普通任务,来,我们看一个,打开我们的代码。呃,来,我们看一下我们的代码哈,我们看一下我们的代码。我们找到ninety server handler。同学们看我们在这里面是不是有个read的操作。
04:03
这个read操作完了过后,是不是他会干什么呀,他会接收到客户呃发送的消息啊,然后呢,他可以打印做客户的地址,现在呢,我们变了,假如说我们这个操作变成一个。非常费时的一个操操作怎么办呢。诶,比如说我们这里,比如比如这里我们有一个有。一个非常耗费,耗费。耗费时间的。业务。你怎么办?你不能让他一直在那傻傻的走吧,所以说我们希望他能够异步执行。亦不自信。那么异步执行呢,其实就是把它提交到,诶提交到该注意听啊,该注意听这句话该channel对应的什么呀,这个nion IO。一般掩体loop。
05:00
的什么呢?他是克。Q中。就可以了,那么现在我要对他进行一个模拟,我们怎么去模拟一个耗时,耗时耗费时间的业务呢?比如说耗耗时。耗时长的业务。那我怎么来做,比如说我有这么一个操作来看一下,嗯,非常的简单啊,我上来过后一个thread,我就让它休眠吗?我模拟时间很长,诶休眠RI。跟上我的思路,Sleep。上面我休眠十秒。乘以100。二乘以1000,然后呢,我往这边回送了一句话。这边也可以回充啊,我们这边再回充另外一句话,比如说这个客户端喵。我们这样这样说,喵,这是第一个喵喵二。大家想,如果我们这样去执行。大家想会有什么后果呢?
06:01
就是我们在读的时候,我们在读的时候读完读完它的,当然我我这里面没有去读它啊,我这边相当于说就不读它的数据了,我直接在这个read啊China read这地方就直接给他回复一个二啊喵二,然后这个read的操操作完了过后,是不是就执行这个喵啊,那么就写个喵一好,我们先来看此时此刻这个情况是什么样子的,好吧,我们先看此时此刻这个情况什样子,然后呢,我在这打一句话。这帮我打一下,叫go on。呃,我那那我这样子大家可以猜测到,呃,如果一个客户端跟服务器端交互,会出现什么情况。同学们想会出现什么情况,是不是客户端那边呢,他会在这个得到这个hello客户端喵二其实是等待十秒才能拿到,而且这个go on呢,也是需要这段代码执行完毕过后才可以去输出go on,而go go on输出完了过后才去走我们的。这个China read completely也是在这个过程中,客户端和服务器端其实在这里是被阻塞了,是这样子吧,那同学们我们运行一下,我先把这边都关闭吧。
07:08
整个关闭了ready吧,同学们好,现在呢,我们先运行一下,看情况啊,走起来。Rag。一个OK。那现在我们注意看这边它会发生一个什么情况,现在服务器端还没有,还没有触发读的事件,因此呢没有显示任何的信息,只说服务器is ready,现在我运行一个客户端运行跑起来。同学们可以看到,此时此刻。此时此刻,看服务器端是不是在这阻塞在的阻塞十秒才会执行这句话,才会执行隔网,是不是他阻塞在这了。是没没任何反应。你看狗是不是狗望完了过后才把这个喵一喵二发回去了,看发了一个。给这边我们看一下。呃,客户端这边得到了一个喵,先得到是喵二,看到没有。
08:02
就是喵二,喵二完了过后输出服务器输出狗二,然后呢,再得到一个哈喵一,哈喵一是嗯,它整个read,呃,China read完毕了过后才发出这个喵一的,因此呢,他先客户端是先收到了喵二,再收到喵一,是这样子的吧。他会阻塞大家看看是不是样子的。好,我们再来再来看一下。再来验证一下,走起来。好阻阻塞在这里没有反应客户端啊,他现在还没阻塞啊。大家可以看到客户端现在没有得到任何消息。十秒过后才能知道。暑下时间。大概会在这得到哈,看拿到了是不是拿到了,好,那现在呢,我们想这样子肯定是不行的,这样不行,为什么呢?因为你如果真的这样子做的话,将来在我们Nike你学完了后,你会发现还是有阻塞呀。
09:01
我们希望这件事情不要堵在这里,你该做什么事情,做什么事情,我们需要需要什么呢?需要异步执行,怎么办好,我们现在说第一个方案,解决方案啊。OK,解决方案一,这个解决方案一呢,就是刚才老师说的这个。用户。程序自定义普通任务来完成。那同学们看我会怎么操作这个事情呢?非常的简单,怎么这样写?首先我们要找到这个event loop。Event lo,因为你你的这个任务是大家看这个图,你所有的这个任务哈,都是在这个n IO event lo里面,你要把它任务提交给他,他对应的这个task q才可以的。解决方案一。我们来继续往下写。那这地方我们怎么去做呢?首先哈,我们还是要获取到,首先第一步我们要想办法拿到我们这个event loop是不是点什么呢?Channel。
10:02
点什么呢?Loop再点SQ的。是吧,在这里呢,我们六一个rene。第一个软这边呢,有一个run,这个run里面呢,我们去把刚才这个代码拿过来,把才我们写的代码再写一遍SH。点力。对,我们休休眠。十秒钟。对,然后呢,我们再输出那句话,那句话呀,就这句话。对吧,那这里呢,他我们为了好看到这边仍然是写个喵二没问题,这边呢,十十秒这边会抛出一个异常,那怎么办?踹一下就可以了。把它踹起来。TRY。如果有异常,我们不会下catch。对吧。然后把一场输出。我们就写个发生异常。
11:01
发生异常。发生异常呢,信息输出就可以了,来ex.get message没问题吧,好,我们这样做完了过后呢,这个东西它就会被提交到哪里呢?提交到当前这个就是跟这个channel。关联的。IO event loop里面的task q里面去执行,那现在我们再来执行一下,看看现在已经是否实现了一个异步的机制来跑起来。好的。跑起来了,跑起来过后呢,我们在运行客户端。好,同学们可以看到,喵一其实马上就得到了。是不是喵一马上得到了,再等十秒钟,喵二就会回来?稍等片刻。哎,苗二又回来了,大家有没有发现这时呢,相当于说我们把这个就是able提交到event的task。呃,这个task q里面去了,而这边呢,大家也再来看一下,这边也不会在阻塞了。
12:04
服务器这端也不会在组上,我们再运行一下,一运行。已运行哈,运行起来等待,然后呢,客户端执行,你们可以可以看到这边马上go on就出现了。是吧,没有主塞在这里,没有主塞在这里,这边呢,喵一马上就拿,也就说他这个相当于说我们这个China read呢,就立马执行完毕,然后呢,呃,他马上就把这个喵一返回去了,是这样子吧,同学们好,这样呢就能够解决这一个,就是我们在这主塞在我们这个handle的某个方法。那下面呢,我们再来验证一下,你说。这个地方就提交到了他是个Q里面,你怎么证明这个事情呢?好,我下一个断点,同学们可以看一下,我再来下一个断点,我在下个断点。我们来用debug的方式再来证明一下,的确是放到这里面去了。来,同学们,我们将其。第八个一把。跑起来。
13:00
没有问题,好跑起来过后呢,各位朋友请看。走一个,我再RUN1把。乱起来。OK,那现在的断点它执行到这里了,我们主要是来看此时此刻在这个event loop里面到底有没有一个任务呢?点这个地方,从这进去看啊。打开。往下走,PI里面有一个China China里面大家可以看到有一个even的lo。是不是IO面的loop呀,点进去再往下面走,它这里面有一个task q。看朋友们,你们看到这面S子是不是等于一。S是不是等于一?诶,说明这边有一个任务,好,那现在呢,为了证明这一点呢,我们再加一个任务,就是有些同学可能说,诶你这个是不是啊,啊对,我可以再给你们加一个任务进去。它这里面可以加多个任务再来看。
14:01
好,我再加一个,这个呢是比如说是20,那么我在这,嗯,问同学们一个问题,就是这个任务是加到我们这个圈子里面去,加进去过后呢,他在十秒过后才执行。呃,他就加进去了,他先执行的是这个,这个执行完了过后才会执行这个任务咯,明白我的意思吧。啊,所以说你在你发,嗯,我问同学们一个问题,就说这个喵二肯定是十秒过后就发回去了,那这个东西秒三是多少,多少秒过后才发送回去呢。是20秒呢还是十加20。同学们理解吗?就是说因为这个任务他在执行的时候,你相当于说你这是第一个在这个任务队里面第一个他在开始执行的时候,他才开始这执行这个十呃休眠,休眠过后呢,在在执行你这个再休眠,这是同一个线程,因此你在这个地方喵二其实是呃十秒过后就发送回客户端,而你这个地方又在原先基础上再休眠了20秒,所以说这个喵三其实是30秒过后才会发送回去。
15:12
能理解这意思吧,那么我们来看一下是不是这样子的,来,朋友们,我们继续来做一个小测试。好不着急啊,同学们,我们先看是不是跟老师说的一样。先运行着。这点一定要注意,他们是在同这个队列,他们是在这个,呃,Task q里面,其实还是在一个线程。走起来。好,我现在署名哈。第一个是妙音。从这开始的走。大概十秒。喵二就会出来,应该在这个位置哈。鸟儿出来了,鸟儿看,从这开始。从这地方开始,你注意发现还要数20秒。为什么?为什么还要数20秒?
16:00
就是因为它是同一个线程,它会继续休眠20秒啊,你看好快了快了。看秒三过来了是吧,大概就是20秒,因为我这可能你看从这开始走嘛,从这开始走,诶到这一个格七秒12秒,再到这呃十嗯嗯17秒,再到大概在这个位置对不对,所以说它其实是在这个基础上再休眠20号,不是说呃不是像大家想象的哦,这个是十秒过后返回这个是20秒。过后返回不要忘了,不要忘了一件事情,他们现在是在同一个线程,明白这意思吧,好,那么我们再来验证一下,是不是当这样做的话,我们这个他Q里面是不是有两个任务呢?来证明一下这个观点,好,这个证明完了过后基本上。这个事情就可以过了。好,我第bug了,然后呢,我运行。跑起来。好的,这是一个客户端跑起来过后呢,我们来此时时刻再来看一下我们task q里面到底有几个,有几个任务走起来。
17:07
往下走。往下走往下走,全往下走,这个lo,注意这个类型是IO。往下走找到我们的这个,他这个Q里面是不是二了。为什么是二,因为你这里面有两个任务是这样子吧,同学们,好的同学们,那关于我们第一个小案例,就是我们这讲的,基于这讲的这个啊,就是用户程序自定义,普通普通任务就搞定。也就是说同学们将来呢,如果你在嗯,这个我们这个pipeline里面的一个handle里面有长时间的操作时候呢,大家可以考虑用这种方式来异步的自信啊,不然的话你就堵在这里了。阻塞这里肯定对我们的并发是有影响的。好,这个我们先说到这儿。
我来说两句