(转) Twisted : 第二部分 异步编程初探与reactor模式

第二部分:低效的诗歌服务器来启发对Twisted机制的理解html

这个系列是从这里开始的,欢迎你再次来到这里来。如今咱们可能要写一些代码。在开始以前,咱们都作出一些必要的假设。python

关于对你的假设react

在展开讨论前,我假设你已经有过用Python写同步程序的经历而且至少知道一点有关PythonSockt编程的经验。若是你从没有写过Socket程序,或许你能够去看看Socket模块的文档,尤为是后面的示例代码。若是你没有用过Python的话,那后面的描述对你来讲可能比看周易还痛苦。git


你所使用的计算机的状况(想的真周到,:)github

我通常是在Linux上使用Twisted,这个系列的示例代码也是在Linux下完成的。首先声明的是我并无故意让代码失去平台无关性,但我所讲述的一些内容确实可能仅仅适应于Linux和其它的类Unix(好比MAC OSXFreeBSD)。WIndows是个奇怪诡异的地方(??为何这么评价Windows呢),若是你想尝试在它上面学习这个系列,抱歉,若是出了问题,我没法提供任何帮助。shell

而且假设你已经安装了近期版本的PythonTwisted。我所提供的示例示例代码是基于Python2.5Twisted8.2.0编程

你能够在单机上运行全部的示例代码,也能够在网络系统上运行它们。可是为了学习异步编程的机制,单机上学习是比较理想的。服务器


获取代码的方法网络

使用git工具来获取Dave的最新示例代码。在shell或其它命令行上输入如下命令(假设已经安装git):异步

git clone git://github.com/jdavisp3/twisted-intro.git

下载结束后,解压并进入第一层文件夹(你能够看到有个README文件)。


低效的诗歌服务器

虽然CPU的处理速度远远快于网络,但网络的处理速度仍然比人脑快,至少比人类的眼睛快。所以,想经过网络来得到CPU的视角是很困难的,尤为是在单机的回环模式中数据流全速传输时,更是困难重重。

咱们所须要的是一个慢速低效诗歌服务器,其用人为的可变延时来体现影响结果。毕竟服务器要提供点东西吗,咱们就提供诗歌好了。目录下面有个子目录专门存放诗歌用的。

最简单的慢速诗歌服务器在blocking-server/slowpoetry.py中实现。你可用下面的方式来运行它。


python blocking-server/slowpoetry.py poetry/ecstasy.txt


    上面这个命令将启动一个阻塞的服务器,其提供“Ecstasy”这首诗。如今咱们来看看它的源码内容,正如你所见,这里面并无使用任何Twisted的内容,只是最基本的Socket编程操做。它每次只发送必定字节数量的内容,而每次中间延时一段时间。默认的是每隔0.1秒发送10个比特,你能够经过 -delay 和 -num-bytes参数来设置。例如每隔5秒发送50比特: python blocking-server/slowpoetry.py --num-bytes 50 –delay 5poetry/ecstasy.txt

当服务器启动时,它会显示其所监听的端口号。默认状况下,端口号是在可用端口号池中随机选择的。你可能想使用固定的端口号,那么无需更改代码,只须要在启动命令中做下修改就OK了,以下所示:


python blocking-server/slowpoetry.py --port 10000 poetry/ecstasy.txt


若是你装有netcat工具,能够用以下命令来测试你的服务器(也能够用telnet):


netcat localhost 10000


若是你的服务器正常工做,那么你就能够看到诗歌在你的屏幕上慢慢的打印出来。对!你会注意到每次服务器都会发送过一行的内容过来。一旦诗歌传送完毕,服务器就会关闭这条链接。

默认状况下,服务器只会监听本地回环的端口。若是你想链接另一台机子的服务器,你能够指定其IP地址内容,命令行参数是 -iface选项。

不只是服务器在发送诗歌的速度慢,并且读代码能够发现,服务器在服务一个客户端时其它链接进来的客户端只能处于等待状态而得不到服务。这的确是一个低效慢速的服务器,要不是为了学习,估计没有任何其它用处。


阻塞模式的客户端

在示例代码中有一个能够从多个服务器中顺序(一个接一个)地下载诗歌的阻塞模式的客户端。下面让这个客户端执行三个任务,正如第一个部分图1描述的那样。首先咱们启动三个服务器,提供三首不一样的诗歌。在命令行中运行下面三条命令:


python blocking-server/slowpoetry.py --port 10000 poetry/ecstasy.txt --num-bytes 30 
python blocking-server/slowpoetry.py --port 10001 poetry/fascination.txt 
python blocking-server/slowpoetry.py --port 10002 poetry/science.txt


若是在你的系统中上面那些端口号有正在使用中,能够选择其它没有被使用的端口。注意,因为第一个服务器发送的诗歌是其它的三倍,这里我让第一个服务器使用每次发送30个字节而不是默认的10个字节,这样一来就以3倍于其它服务器的速度发送诗歌,所以它们会在几乎相同的时间内完成工做。

如今咱们使用阻塞模式的客户端来获取诗歌,运行以下所示的命令:


python blocking-client/get-poetry.py 10000 10001 10002


若是你修改了上面服务口器的端口,你须要在这里时行相应的修改以保持一致。因为这个客户端采用的是阻塞模式,所以它会一首一首的下载,即只有在完成一首时才会开始下载另一首。这个客户端会像下面这样打印出提示信息而不是将诗歌打印出来:


Task 1: get poetry from: 127.0.0.1:10000
Task 1: got 3003 bytes of poetry from 127.0.0.1:10000 in 0:00:10.126361
Task 2: get poetry from: 127.0.0.1:10001
Task 2: got 623 bytes of poetry from 127.0.0.1:10001 in 0:00:06.321777
Task 3: get poetry from: 127.0.0.1:10002
Task 3: got 653 bytes of poetry from 127.0.0.1:10002 in 0:00:06.617523
Got 3 poems in 0:00:23.065661


这图1最典型的文字版了,每一个任务下载一首诗歌。你运行后可能显示的时间会与上面有所差异,而且也会随着你改变服务器的发送时间参数而改变。尝试着更改一下参数来观测一下效果。


异步模式的客户端

如今,咱们来看看不用Twisted构建的异步模式的客户端。首先,咱们先运行它试试。启动使用前面的三个端口来启动三个服务器。若是前面开启的尚未关闭,那就继续用它们好了。接下来,咱们经过下面这段命令来启动咱们的异步模式的客户端:


python async-client/get-poetry.py 10000 10001 10002


你或许会获得相似于下面的输出:
Task 1: got 30 bytes of poetry from 127.0.0.1:10000 
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
Task 3: got 10 bytes of poetry from 127.0.0.1:10002
Task 1: got 30 bytes of poetry from 127.0.0.1:10000 
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
...
Task 1: 3003 bytes of poetry
Task 2: 623 bytes of poetry
Task 3: 653 bytes of poetry
Got 3 poems in 0:00:10.133169

此次的输出可能会比较长,这是因为在异步模式的客户端中,每次接收到一段服务器发送来的数据都要打印一次提示信息,而服务器是将诗歌分红若干片断发送出去的。值得注意的是,这些任务相互交错执行,正如第一部分图3所示。

尝试着修改服务器的设置(如将一个服务器的延时设置的长一点),来观察一下异步模式的客户端是如何针对变慢的服务器自动调节自身的下载来与较快的服务器保持一致。这正是异步模式在起做用。

还须要值得注意的是,根据上面的设置,异步模式的客户端仅在10秒内完成工做,而同步模式的客户端却使用了23秒。如今回忆一下第一部分中图3与图4.经过减小阻塞时间,咱们的异步模式的客户端能够在更短的时间里完成下载。诚然,咱们的异步客户端也有些阻塞发生,那是因为服务器太慢了。因为异步模式的客户端能够在不一样的服务器来回切换,它比同步模式的客户产生的阻塞就少得多。


更近一步的观察

如今让咱们来读一下异步模式客户端的代码。注意其与同步模式客户端的差异:

 

1.异步模式客户端一次性与所有服务器完成链接,而不像同步模式那样一次只链接一个。

2.用来进行通讯的Socket方法是非阻塞模的,这是经过调用setblocking(0来实现的。

3.select模块中的select方法是用来识别是其监视的socket是否有完成数据接收的,若是没有

它就处于阻塞状态。

4.当从服务器中读取数据时,会尽可能多地从Sockt读取数据直到它阻塞为止,而后读下一个Sockt

接收的数据(若是有数据接收的话)。这意味着咱们须要跟踪记录从不一样服务器传送过来诗歌的接

收状况(由于,一首诗的接收并非连续完成,因此须要保证每一个任务的可连续性,就得有冗余

的信息来完成这一工做)。

异步模式中客户端的核心就是最高层的循环体,即get_poetry函数。这个函数能够被拆分红两个步骤:

1.使用select函数等待全部Socket,直到至少有一个socket有数据到来。

2.对每一个有数据须要读取的socket,从中读取数据。但仅仅只是读取有效数据,不能为了等待还没

来到的数据而发生阻塞。

3.重复前两步,直到全部的socket被关闭。


能够看出,同步模式客户端也有个循环体(在main函数内),可是这个循环体的每一个迭代都是完成一首诗的下载工做。而在异步模式客户端的每次迭代过程当中,咱们能够完成全部诗歌的下载或者是它们中的一些。咱们并不知道在一个迭代过程当中,在下载那首诗,或者一次迭代中咱们下载了多少数据。这些都依赖于服务器的发送速度与网络环境。咱们只须要select函数告诉咱们那个socket有数据须要接收,而后在保证不阻塞程序的前提下从其读取尽可能多的数据。

若是在服务器端口固定的条件下,同步模式的客户端并不须要循环体,只须要顺序罗列三个get_poetry

就能够了。可是咱们的异步模式的客户端必需要有一个循环体来保证咱们可以同时监视全部的socket端。这样咱们就能在一次循环体中处理尽量多的数据。

这个利用循环体来等待事件发生,而后处理发生的事件的模型很是常见,而被设计成为一个模式:reactor模式。其图形化表示如图5所示:

第二部分:异步编程初探与reactor模式


这个循环就是个”reactor“(反应堆),由于它等待事件的发生然对其做为相应的反应。正由于如此,它也被称做事件循环。因为交互式系统都要进行I/O操做,所以这种循环也有时被称做select loop,这是因为select调用被用来等待I/O操做。所以,在本程序中的select循环中,一个事件的发生意味着一个socket端处有数据来到。值得注意的是,select并非惟一的等待I/O操做的函数,它仅仅是一个比较古老的函数而已(所以才被用的如此普遍)。如今有一些新API能够完成select的工做并且性能更优,它们已经在不一样的系统上实现了。不考虑性能上的因素,它们都完成一样的工做:监视一系列sockets(文件描述符)并阻塞程序,直到至少有一个准备好时行I/O操做。


严格意义上来讲,咱们的异步模式客户端中的循环并非reactor模式,由于这个循环体并无独立于业务处理(在此是接收具体个服务器传送来的诗歌)以外。它们被混合在一块儿。一个真正reactor模式的实现是须要实现循环独立抽象出来并具备以下的功能:

 

1.监视一系列与你I/O操做相关的文件描述符(description)

2.不停地向你汇报那些准备好I/O操做的文件描述符


一个设计优秀的reactor模式实现须要作到:

1.处理全部不一样系统会出现的I/O事件

2.提供优雅的抽象来帮助你在使用reactor时少花些心思去考虑它的存在

3.提供你能够在抽象层外(treactor实现)使用的公共协议实现。


好了,咱们上面所说的其实就是Twisted—健壮、跨平台实现了reactor模式并含有不少附加功能。

在第三部分中,实现Twisted版的下载诗歌服务时,咱们将开始写一些简单的Twisted程序。

相关文章
相关标签/搜索