00:02
Handler和handler contact创建源码剖析。通过全面的讲解,我们知道在light里边呢,有三个非常核心的组件,一个是China pipeline,一个是China handler,还有一个是China handle contact是非常核心的。我们现在呢,从源码的角度来分析ni是如何设计这三个核心组件的,并分析它是如何创建和协调工作的。下面呢,我们来做源码级的剖析。首先看。在分析过程中呢,我们会有很多的图形,所以说我们已经把这个文档呢准备好了。一边来看这个文档,一边呢对我们的源码进行一个debug,或者是对源码的阅读,那现在呢,我们先把。打开。先把准备好的文档打开,我们看一下。我们分成这么几个部分来看哈,我们先给同学们看,第一个就是先把他们的关系捋一捋。
01:05
其实它们之间的关系呢,在前边我们已然是讲过了,这里呢再给他过一遍,完把它过完了,过后呢,我们先给大家讲China paper的作用以及它的设计。然后呢,我们再讲China handler,再讲China handler context,对吧?我们按照这条线来进行讲解,首先我们先来了解一下三者的关系。每当server创建一个新的连接,就是我们服务器端跟客户端创建一个连接过后呢,就会对应生成一个so。对,因为我们Nike呢,它仍是基于我们这个TCP的,所以说最终呢还是有soet的,对应的就是目标客户端,第二每一个新创建的socket都会分配一个全新的China paperline。下面呢,我们就直接简称排line,也就是说其实我们服务器端跟客户端对应的这个soet呢,都会对应每每一个socket都会对应一个全新的China pipeline。
02:10
第三点,每一个China派内部含有多个China handle contact,下面呢,我们就简称contact。就说是这样的一个关系。它们之间呢,组成了双向链表。这个双线链表我们在前面已经说过了,只是呢,当时讲的还不是非常的细节,现在我们从源码级进行细节讲解。这些context就是大家看到的这些context。Contest。用于包装。我们调用ADD last方法是添加的handler。明白的意思,所以说从上面这个图呢,我们可以得出这样几个结论,从上图我们可以看到China socket和China pipeline是一一对应的关系,大家可以看这边。是不是有一个China socket对应China?
03:02
而PI内部呢,有多个什么呀?形成了链表,注意这个链表呢,其实它是一个双向的。双向的对,它是一个双向的,待会我们再具体说哈,对,那么所以说我们说contact,它是对我们handler的一个封装。当有一个请求进来的时候,会进入到socket对应的pipeline,就说你比如说你这有个客户端,一个请求通过socket来了。那来了以后呢,会经过我们这个拍烂。呃,经过拍,拍了所有的handler。这样一个过程,其实这样一个过程呢,从设计模式的角度来讲,就是我们的过滤器模式。大家应该对过滤器模式有一些了解。好的,那现在呢,我们对他三者,对三者的关系做了一个梳理过后呢,我们就来一个一个一个一个的看,我们先来看China pipeline。
04:01
对,我们来看一下。China paperline的接口呢,它本身是个接口,我们从它的源码可以看到China paperpe呢,它其实是继,呃,它继承了terable,还有China inbound in invoer和China outbound invoer,我们看一下源码。来走一个,现在呢,我们看的是China。China pipe。OK,穿着拍了就他了。来看一下是不是这样一个关系,大家看这是个接口,该接口呢,继承了另外几个接口,是不是一个inbound invoer outbund invoer,还有interable,那么有了这些接口,它能做什么事情呢?我们继续往下分析,这是它源码哦,我们把这源码一打开。这这个关系哈,它的一个结构。大家有没有发现这个China pipeline有很多这样的方法,At first。
05:03
At first,还有at last at last before。ADD after,诶,大家有没有看到这个是添加到最前面,这个是添加到链表的最后面,还有什么呀?I before和I up after,这个是不是相当于添加,呃,插入到某个位置啊。是不是就是把你的这个channel handler插入到整个双向链表的某个位置也是可以的?对吧,还可以干什么呢,Remove。看到没有,Remove。诶还有什么replace,诶还有replace,也就是说我们这个China派呢,对我们整个这一个链表。这个China contact链表可以进行很多的操作,哪些操作呢?可以进行。添加可以加在这个最前边,但是最前边的其实这个头啊,是默认生成的,不能动,要添加也是添加到这个头的第一个位置,也可以加在我们的最后面,最后这个呢,是个T也是默认的不能动,你添加的最后其实是添加到这个t contact的前面这个这个节点我们也可以加,插入到中间某个位置,还可以干什么呢?还可以把其中某一个contact进行一个什么呀,进行一个替换。
06:17
就是这个哦,Handler进行一个替换,看到没有,就是把它的一个handler行一个替换也是可以的。好,接着继续往下走。那这是它的部分源码,我们可以看到该接口继承的inbound outbound inter,那么它表示它可以调用数据出站的方法和入站的方法。也就是说这个全派呢,既可以对我们出站进行操作,也可以进对入站进行操作,为什么呀,因为它这里面含有入站的handler,也含有出站的handler。同时它也可以便利内部的链表,为什么呢?因为它在这里继承了itable,我们看itable里面呢,它有一个非常重要的方法叫for each。来,我们看一下。
07:00
找到这边来,我们追到这边去看一下a terrible。那们看里面是不是有for啊。对不对,他是不是代表就是说你是实际上是可以进行便利的,没问题吧,这是Java基础就告诉我们的了,继续往下分析,继续往下分析。从它几个代表性的方法我们可以看到,基本上都是针对handle链表的插入、追加、删除、替换等,所以说它类似于一个link list。对,同时呢,它也可以返回China,也就short,大家有发现他在这个China派里面呢,它也可以返回China,那这个地方我们可以看它的一个时限内,或者看他自己我们看。往这儿走。往这走,他这边有没有一个China呢。往下看好。哎,大家看。是不是他还有一个。一个方法,这个方法当然要由它下面的实现子类来做,看它下面实现子类很多的对不对。
08:02
好,那这个地方我们回到这儿。也就是说我们China派呢,也可以返回它对应的China。接着继续往下分析。往下分析。在这个paperline的接口文档上,它提供了一个图,这样一个图就看的更清晰了,大家有发现在China paperline那呢,我们可以进行一个入站的操作。也可以进行一个出站的操作,我曾经讲过入站和出站你怎么记。你怎么记这个东西呢?你可以这样理解,如果我们的数据,如果我们的数据或者事件是从这个so的向我们的拍拍烂流动,我们就称之为入站。如果我们这个世界是从这个chinaline向我们so这边出的啊,往这边出来的,那么我们称称之为出站。这个大家要理解好。这是一个图,但是呢,这个图很容易让人产生一种误会,产生什么误会呢?好像认为好像这种图,这个图啊,它很容易让人理解成我们的China派的链表是长成这个样子的。
09:13
很容易哈,长成,长成这边是一条线。这是一个列表。啊,然后呢,下面又有一个链表,好像是这样一种链表,很容易让人让人产生这种误会呢,为什么?因为他诶很多同学一看这个图,认为这边是我们的入站handler链表,这边是我们的出站handle列链表,然后呢,被我们整个这个China包起来,其实这样是这样认为是不对的。这样认为是不对的,待会儿呢,我们看可以通过源码能看到他其实是这样做的。其实这个我在前面已经说过了,只是这里再强调一下,它真实的是前面有个head。这个是默认的,所有的事件,不只要是所有的事件,只要是我们的这个,呃,入站的这个操作都要经过hide。
10:03
好,然后,然后大家看所有的实验都要经过它啊,所有实验都要经过它,它其实是这样子,一个一个流程。这边是个tail。他其实是这样子,头往这边走,往这边走,往这边走,然后往这边走,往这边走,往这边走,往这边走,也就是说我们这个出战的handle德,还有入战的handle,其实都是在这一个列表里面。当然了,有同学会说,诶老师不对呀,你不是说的,在入站的时候它只调用inbound handler,在出站的时候,这边是出站的方向,它只调用out bund out bund handle吗?那它怎么做到的呢?大家不要忘了,在我们的这一个contact里面,我待会会给大家追源码,这个contact里面呢,它有一个就是一是呃,就是英镑的和auto棒的一个标,一个属性来判断,也就是他在出,比如他在出战的时候,他会怎么做呢?他会先看一下他,他肯定会去先找下一个,他去找下一个出战的handler,他先判断你这个是不是一个出战的handler,如果不是,他再找下一个处理,直到他找到一个出战的handler处理,处理完了过呢,他再找下一个出战的handler,明白我的意思吧,入站也是一样的。
11:21
待会呢,同学们可以看到这段源码,我待会儿会把源码给你们找出来,大家不要着急,待会儿我给你看源码,它到底是怎么去进行handle的调度的,不管是入站还是出站,待会呢,我们用源码来给他解释它是怎么做的,其实它是一个外循环。啊,比如他在循环的时候,他现出站的话,他就先找一定要找到一个出站的汉字,如果入站的话呢,他找到一个入站汉字调用,调用完了过后再找下一个。OK,好,大家理解这个东西啊,不要认为这是两条链表啊,其实是一条链表,这条链表呢,只是双向的而已,好,继续往下走。那么对于上图这个理解呢,我们来看一下这个图的理解啊,同学们看。
12:04
对,上图这这是一个handleler的list handle呢,用于处理或者拦截入战事件或者出战事件。排on nine呢行实现了过滤器的高级形式,以便用户控制事件,就说以便用户控制事件如何处理,以及handle格,呃,处理什么呢?这个这个处理handler在pipeline中相如何交付的。上图描述了一个典型的handle在PA中处理IO事件的方式,IO事件由inbound handle或者outbound handle处理并调用什么呢?大家看,我马上给你们看这个方法了,它是调用我们这个China handle contact里面的一个fire。China read来处理,也就是说我们在进行这个入站和出站的时候,它真实的是调用他先找找这个handle contact里那个fire read handle,那么现在我们来看一下这个东西到底是怎么来玩的,来,朋友们。
13:05
给大家搂一眼。好,同学们给大家揉页,在这里看一下。朋友们。朋友看是不是在最下面有这有一个接口叫China hundred。对,那下面呢,我们就找一个他的。它的一个实现的子类。好,大家看这里看到没有。诶,他在进行这个fire China read的时候呢,它是怎么做的呀,同学们看他怎么做的,他是不是invoke China read这个方法,在这个方法调用这个invoke invoke China read的方法的时候,他首先要find contact inbound。诶,大家看。也就是说,也就是说他在地方,他去先找一个。这个bound的,呃,当然如果你这个是进行这个处理的时候,他是找的入站的啊,就是fund fund的。
14:01
这个contact也是他找下一个,下一个入站的什么呢?入站的handler,那么看他是怎么做的,来,朋友们进到这个源码里面去哦,一目了然,大家看。看懂了吗?同学们,他怎么做的?是不是他因为你现在是入站,入站的话,大家都知道,我们这个是从头是入站,是从这个头开始走。往下走,所以说这个呢,就应该是个next。大家看到没有,他从这个ctx走next。找next就找下一个,但是找的时候呢,它有个条件,就是你必须是一个什么呀,是一个Bo的。大家看,如果你这个是个英镑的的这个地方,这个地方你是个英镑的就是错的话。是一个错的话,是不是我就退出这个读外循环了。大家明白我的意思吗?就说如果相当相当于说我先把这个当前的CTXNE这个handler先拿到。先拿到或者是呃,不是handle的啊,是一个。
15:04
这个肯定是因为我们知道链表整个真正的节点类型是context的,他先看下一个contact的是他,他先拿一个下一个,因为你是入战嘛,先把这个取出来,看看你是不是一个inbound的,如果是inbound的呢,我就退出来,相当于我找到了一个,那如果你这下一个它不是一个inbound的handle,那个handle德尔怎么办呢?它就继续往下涨。看到没有,最喜这,直到他找到一个是入战的这个handler及入战的一个contact。那再返回。所以说这个find的contact in镑und是这样子的,那当然有find contact in镑und的就有find contact outbound,大家看这段代码。诶,这段代码是不是处理我们出战的时候的一个方式。大家知道出战的时候呢,测是从这个T开始处理的,大家还有印象吧?
16:03
是不是从这个方向开始处理的啊,当然这个是双向链表了,他出站的时候处理,那么他怎么做呢?他先找先把这个前面这个拿到,因为你你是从尾往前走,他取出当前这一个context的前一个contact。判断你是不是一个奥棒的。诶是不是一个奥特曼的,如果是一个奥特曼德,我就结束了,相当于说我找到一个,我从这个链表里面,从我们这个链表里面,我就找到了一个出战的。出战的这个context。那我就返回,如果我在从这个tell这方开始找,我没有下一个,比如这个下一个不是一个出战的奥und怎么办呢?我再往前面找,看看是不是直到找到一个理解,好同学们,我们在前面是不是讲过auTo Boundund的这个autobound和in磅的这个这个参数啊,来给大家debug一下。
17:03
我给同学们debug一下,大家不着急啊。走到这里,我们把断点下到这儿吧。下在这个位置问题吧,同学们,然后呢,我们现在来进行debug。第八个我们来看一下,就是在ctx这块。Ctx这块它到底有没有我们刚才所说的inbound和outbound的这个属性,对来我们看一下。那现在呢?第八个。跑起来。OK。好,我们跑起来,跑起来过后呢,现在我们打开一个浏览器,发出一个请求,对吧,我们是不是应该发出一个请求才可以。走起来。HTTP。Host,我们是8007回撤。
18:02
一旦回车,断点就下在这里,我们把光标放在ctx上面去,我们看一下。同学们看这里是不是?是不是这边已经看到了有一个handler。对不对,Handler server handler。然后呢,我们往下看下面这个地方inbound诶当前我们这个ctx是不是一个inbound呀。那你看确实嘛,你继承了China in Bo嘛,这样子的,对,所以他还可以继续往下走了,对不对,下面如果还有的话,因为我目前呢,呃,再往下面走,这有一个结尾的叫tell contact,下面呢就没有了,所以说这个就是我们当前这个下一个就已经到了t context。所以说这就是我们刚才所说的。在哪里呢?在我们这个方法里边,它根据这个outbound或者是inbound来判断你当前是一个怎样,是一个入战的,还是一个出战的。
19:02
是这样来做的,继续往下分析。那么入站事件呢?有入站处理程序,自下而上方向处理,已知如果针对这个图的话,入站是自下。而上。如图所示,入账处理程序通常处理由图底部的IO啊IO线程生成的入账数据,而入站数据通常是从哪里拿到的呢?是从shocked。China read的方法来读取。实际上就是我们可以通过。这里所说的这种方法来获取。是不是?Read。好,接着继续往下分析。嗯,通常一个PA呢,有多个handler,这个我就不说了,肯定是有多个的,例如一个典型的服务器,在每个通道的管道中都会有以下处理程序,哪几个呢?一个是。解码器是不是说过就是我们所说的这一个decoder,它通常呢,会把二进制数据转成Java数据,Java对象还有一个解码器,那解码器是干什么的呢?
20:10
啊,这个编码器,编码器就是我们说所说的nco,它是将加入对象转成二进制,这这些内容我们在前面已经讲过了。还有一个就是业务处理,就是业务逻辑处理程序就执行执行实际业务的,比如说对数据库访问等等。那么在这里我们要强调一下,同学们一定要注意我们的业务程序。不能将线程阻塞,会不能在这个线程阻塞在这里会影响IO的速度,从而影响整个Nike程序的性能。如果你的业务程序很快可以放在IO线程中,反之,你需要进行异步处理,怎么处理呢?这句话的意思我跟同学们解解读一下,就是说比如我们这读取数据了,对吧,我们读取数据以后,如如果你要对这个数据进行处理,而且你处理的时候呢,也是一个非常耗时的业务,通常情况下不要把你的代码直接写在read啊,Read这个位置应该怎么做呢?同学看这里我们应该进行异步处理。
21:15
大家还记不记得,我们在前面其实已经讲过怎么去处理比较耗时的业务处理了。还记得这个意思吧?会怎么样呢?一般我们是异步处理这样做,在在添加handle时候,我们可以添加一个线程词,比如这样做pipeline pipeline.a group,我们把这个费时handler直接向这个group里面加入。这样就是形成一个线程式,进行异步处理。或者按照我们以前所说的,把它加入到我们一个任务里面去,还记得吗?就是那个任务我们不是讲过,在我们的整个整个程序里边。这个这这这个地方看不到啊。就是在在我们的这个程序里,在round的这个event的group group里边,它其实呢,也有相应的什么样,它有相应的这个东西,我看这能不能看到。
22:13
这个地方能不能看到啊,看一下pipeline,应该从pipeline里面可以看到。拍啊,这一面还China,再到China里面看看能不能找到啊,就是要放到哪里去呢。我来找一下a even the loop。对,Even the loop。放到他的这里面去。就他。Q或者是schedule,它是个Q。在这里。是不是还有这个东西啊。诶,在这儿往下找一找。要找一个这儿。放到我们task这里面去。好,这是我们所说的这这句话的含义。那关于现在呢,我们关于第一个部分就是哪一块呢?就是China pipeline的设计和它的源码,我们就说到这里。
我来说两句