-
Twisted
2019-09-24 17:08:14Twisted Twisted是以高性能为目标的异步网络编程框架。每一个路由器对应一个reactor twisted是一个用python语言写的事件驱动的网络框架,他支持很多种协议,包括UDP,TCP,TLS和其他应用层协议 Reactor reactor是...-
Twisted
Twisted是以高性能为目标的异步网络编程框架。每一个路由器对应一个reactor
twisted是一个用python语言写的事件驱动的网络框架,他支持很多种协议,包括UDP,TCP,TLS和其他应用层协议 -
Reactor
reactor是事件管理器,用于注册、注销事件,运行事件循环,当事件发生时调用回调函数处理。
1. Twisted的reactor只有通过调用reactor.run()来启动。
2. reactor循环是运行在主进程中。
3. 一旦启动,就会一直运行下去。reactor就会在程序的控制下(或者具体在一个启动它的线程的控制下)。
4. reactor循环并不会消耗任何CPU的资源。
5. 不需要显式的创建和安装reactor,只需要引入就OK了
6. 在Twisted中,reactor是Singleton(也就是单例模式),即在一个程序中只能有一个reactor,并且只要你引入它就相应地创建一个
7. reactor是管理twisted框架的核心
8. 所有的事件都会触发reactor,然后他会开启服务,初始化factory,factory再初始化protocol。 -
Factory
保存的是连接方的信息,当有链接过来时,factory会初始化一个protocol与对方建立连接,所以factory中有protocol. 因为protocol的方法经常会用到factory里的属性变量,所以protocol类中也有factory。这里就有一个循环引用的问题
protocol说白了就是你自己编的程序
-
应用场景:
控制器的探针把自己模拟成一台路由器,不停的与拓扑中的其他路由器发送和接收bgp报文,并根据收到的不同报文做响应处理,python使用struct来处理二进制报文,struct.pack(二进制数)解析成各种类型 -
FSM:
类似etcd,当收到keep alive报文的类型是ST_OPENCONFIRM后,将status状态改为EST,并记录下当前时间保存在数据库
-
-
twisted
2018-05-16 21:37:59示例一 示例二 ...from twisted.web.client import getPage from twisted.internet import reactor from twisted.internet import defer def callback(result): print('下载完成了',result)...示例一
from twisted.web.client import getPage from twisted.internet import reactor from twisted.internet import defer def callback(result): print('下载完成了',result) return result # d = Deferred对象 # 对象内部封装了:url=http://www.bing.com 和 callback # 为本次请求创建socket对象,添加到while循环中。 d = getPage(bytes('http://www.bing.com', encoding='utf8')) d.addCallback(callback) def stop(): reactor.stop() # 监听所有的Deferred对象=socket对象,如果列表中所有的Deferred对象=socket对象都已经下载完成 dl = defer.DeferredList([d,]) # 那么就执行stop函数 dl.addBoth(stop) reactor.run()
示例二
from twisted.web.client import getPage from twisted.internet import reactor from twisted.internet import defer def callback(result): print('下载完成了',result) return result @defer.inlineCallbacks def task(): # d = Deferred对象 # 对象内部封装了:url=http://www.bing.com 和 callback # 为本次请求创建socket对象,添加到while循环中。 d = getPage(bytes('http://www.bing.com', encoding='utf8')) d.addCallback(callback) yield d def stop(): reactor.stop() dlist = [] for i in range(1): d = task() dlist.append(d) # 监听所有的Deferred对象=socket对象,如果列表中所有的Deferred对象=socket对象都已经下载完成 dl = defer.DeferredList(dlist) # 那么就执行stop函数 dl.addBoth(stop) reactor.run()
示例三
from twisted.web.client import getPage from twisted.internet import reactor from twisted.internet import defer def gggg(result): print('下载完成了',result) return result # 如果创建一个 Deferred 对象,表示:创建了一个永远不可能完成的任务 # 用户如果主动调用:d.callback("asdfasdf"),表示手动完成任务了。 d = defer.Deferred() d.addCallback(gggg) reactor.run()
示例四
#!/usr/bin/env python # -*- coding:utf-8 -*- from twisted.internet import defer from twisted.web.client import getPage from twisted.internet import reactor import threading counter = 0 def callback(arg): print("arg----",arg) global counter counter += 1 if counter == 5: _closewait.callback(None) print('one', len(arg)) def funcc(args): print("_",args) reactor.callLater(0, _next_request) def _next_request_from_scheduler(): ret = getPage(bytes('http://www.chouti.com', encoding='utf8')) # ret.addCallback(callback) # ret.addCallback(funcc) # ret.addCallback(lambda _: reactor.callLater(0, _next_request)) def _next_request(): _next_request_from_scheduler() ############################### _closewait = None @defer.inlineCallbacks def task(url): reactor.callLater(0, _next_request) yield engine_start() @defer.inlineCallbacks def engine_start(): global _closewait _closewait = defer.Deferred() yield _closewait ################################# def stop(arg): print('已经全部完成') reactor.stop() if __name__ == '__main__': url = 'http://www.cnblogs.com' defer_list = [] deferObj = task(url) defer_list.append(deferObj) v = defer.DeferredList(defer_list) v.addBoth(stop) reactor.run()
示例五
#!/usr/bin/env python # -*- coding:utf-8 -*- from twisted.web.client import getPage, defer from twisted.internet import reactor import queue class Response(object): def __init__(self, body, request): self.body = body self.request = request self.url = request.url @property def text(self): return self.body.decode('utf-8') class Request(object): def __init__(self, url, callback=None): self.url = url self.callback = callback class Scheduler(object): def __init__(self, engine): self.q = queue.Queue() self.engine = engine def enqueue_request(self, request): self.q.put(request) def next_request(self): try: req = self.q.get(block=False) except Exception as e: req = None return req def size(self): return self.q.qsize() class ExecutionEngine(object): def __init__(self): self._closewait = None self.running = True self.start_requests = None self.scheduler = Scheduler(self) self.inprogress = set() def check_empty(self, response): if not self.running: self._closewait.callback('......') def _next_request(self): while self.start_requests: try: request = next(self.start_requests) except StopIteration: self.start_requests = None else: self.scheduler.enqueue_request(request) while len(self.inprogress) < 5 and self.scheduler.size() > 0: # 最大并发数为5 request = self.scheduler.next_request() if not request: break self.inprogress.add(request) d = getPage(bytes(request.url, encoding='utf-8')) d.addBoth(self._handle_downloader_output, request) d.addBoth(lambda x, req: self.inprogress.remove(req), request) d.addBoth(lambda x: self._next_request()) if len(self.inprogress) == 0 and self.scheduler.size() == 0: self._closewait.callback(None) def _handle_downloader_output(self, body, request): """ 获取内容,执行回调函数,并且把回调函数中的返回值获取,并添加到队列中 :param response: :param request: :return: """ import types response = Response(body, request) func = request.callback or self.spider.parse gen = func(response) if isinstance(gen, types.GeneratorType): for req in gen: self.scheduler.enqueue_request(req) @defer.inlineCallbacks def start(self): self._closewait = defer.Deferred() yield self._closewait def open_spider(self, spider, start_requests): self.start_requests = start_requests self.spider = spider reactor.callLater(0, self._next_request) class Crawler(object): def __init__(self, spidercls): self.spidercls = spidercls self.spider = None self.engine = None @defer.inlineCallbacks def crawl(self): self.engine = ExecutionEngine() # 创建爬虫对象 self.spider = self.spidercls() start_requests = iter(self.spider.start_requests()) #start_requests = iter(start_requests) self.engine.open_spider(self.spider, start_requests) yield self.engine.start() class CrawlerProcess(object): def __init__(self): self._active = set() # {d,} self.crawlers = set() # Crawler对象(ChoutiSpider类) def crawl(self, spidercls, *args, **kwargs): # spidercls=ChoutiSpider类 crawler = Crawler(spidercls) self.crawlers.add(crawler) # 永远不可能完成的deferred对象 d = crawler.crawl(*args, **kwargs) self._active.add(d) return d def start(self): dl = defer.DeferredList(self._active) dl.addBoth(self._stop_reactor) reactor.run() def _stop_reactor(self, _=None): reactor.stop() class Spider(object): def start_requests(self): for url in self.start_urls: yield Request(url) class ChoutiSpider(Spider): name = "chouti" start_urls = [ 'http://dig.chouti.com/', ] def parse(self, response): print(response.text) if __name__ == '__main__': crawler_process = CrawlerProcess() crawler_process.crawl(ChoutiSpider) crawler_process.start()
-
twisted资料twisted资料
2010-09-16 23:55:35twisted资料twisted资料twisted资料 -
Python Twisted
2018-12-10 18:22:00Twisted是用Python实现的基于事件驱动的网络引擎框架。 -
Expert Twisted
2019-05-16 08:57:302019年新书,介绍了Python的异步和网络库twisted库的用法,适用于python异步和事件驱动开发者参考。 -
Twisted 17.9.0
2018-12-20 16:40:03twisted 17.9.0的whl安装包,包含python2.7 3.4 3.5 3.6 的32位和64位包 -
twisted python_python twisted教程 三–开始twisted
2020-11-25 17:05:45原文:http://krondo.com/blog/?p=1209 Chinese by Yang Xiaowei and Cheng Luo 什么也不做,the ...但首先让我们写一些twisted代码找找twisted 的感觉.下面是一个简单到屁的twisted程序,源码在basic-twisted/simple....原文:http://krondo.com/blog/?p=1209
Chinese by Yang Xiaowei and Cheng Luo
什么也不做,the twisted way
最后我们决定重新用twisted来实现我们异步的读诗的客户端.但首先让我们写一些twisted代码找找twisted 的感觉.
下面是一个简单到屁的twisted程序,源码在basic-twisted/simple.py
from twisted.internet import reactor
reactor.run()
你可以像这样运行它:
python basic-twisted/simple.py
就像我们在第二部分所说的,twisted 是一个反应器模式的实现,包含一个反应器或者被叫做事件循环的对象,这个对象是twisted 程序的核心.在上面的程序中的第一行我们导入reactor,第二行告诉reactor去执行一个事件循环.
这个程序事实上什么也没做,你可以用Control-C停止他,否则的话他会永远执行下去.一般来说我们应该给事件循环一个或者多个文件描述符让事件循环来监测I/O,我们后面会讲到该怎么做,但现在反应器的事件循环是卡住的,记住反应器的事件循环不是一个不停循环的繁忙的循环,如果你可以监测cpu的使用情况,你可以发现这个reactor 是不耗费cpu资源的,这个反应器的事件循环会在图片五的顶部卡住,等待将要来的事件.
这个程序仍然是一个相当简单的程序,完全没有吸引力是吧? 我们接下来会做点有趣的事,不过我们已经可以得出下面的结论:
twsited 的reactor 不会自动开始运行,你可以让它运行起来通过reactor.run()
反应器的循环(reactor loop)和它开始运行的地方运行在同一线程内,在这种情况下,他运行在主线程内
一但事件循环运行起来,就会永远运行下去.这时reactor受程序控制
如果没有什么事情可做,反应器的循环不耗费CPU资源
reactor 不用创建,直接import进来就可以用
最后一点需要解释一下,在twisted中, reactor 是单例模式的.在一个twisted程序中只有一个reactor object,在你用import导入的时候就被创建了.如果你打开reactor 的源码,你会发现只有很少的代码,reactor 的真正实现在twisted.internet.selectreactor.
twisted 中有多种reactor 的实现.在第二部分已经提到,select 仅仅是其中的一中实现方法,也是twisted默认使用的方法.twisted中也包含其他的reactor 的实现方式,比如twisted.internet.pollreactor使用poll 的方式来实现.
如果要使用其他的reactor,你必须要在导入twisted.internet.reactor前先安装一下,你可以这样来安装pollreactor:
from twisted.internet import pollreactor
pollreactor.install()
如果你在导入twisted.internet.reactor之前没有安装其他的reactor 实现,twisted默认选择selectreactor.所以一般的不要在一个模块的上层导入reactor用来避免不经意的安装了默认的reactor,相反的,要在你要引用reactor 的时候再去导入它.
Note:自从写这些文章开始,twisted正在慢慢的转向允许多种reactor 同时存在的结构(应该是这样翻译),在计划中,一个reactor对象被传递的时候只作为一个引用而不是从一个模块中导入.
Note:不是所有的操作系统都支持poll,如果你的系统不支持,上面的例子在你的系统跑不起来
现在我们可以用pollreactor重新实现我们的第一个 twisted 程序,你可以在basic-twisted/simple-poll.py看到源码:
from twisted.internet import pollreactor
pollreactor.install()
from twisted.internet import reactor
reactor.run()
好吧,我们又非常成功的实现了一个什么也不做的poll 循环.
我们在以后的教程中都会使用使用twisted 的默认的reactor,为了学习twisted的话,所有的reactor都执行相同的功能.
Hello Twisted
下面让我们实现一个做点东西的twisted程序,下面的程序会打印两行消息:
def hello():
print 'Hello from the reactor loop!'
print 'Lately I feel like I'm stuck in a rut.'
from twisted.internet import reactor
reactor.callWhenRunning(hello)
print 'Starting the reactor.'
reactor.run()
程序代码在 basic-twisted/hello.py,如果你运行的话,会看到以下的输出:
Starting the reactor.
Hello from the reactor loop!
Lately I feel like I'm stuck in a rut.
需要你手动杀死这个程序,因为当执行完print 之后程序会卡住.
hello 函数是在reactor 运行之后被调用的,这就意味这hello函数是被reactor调用的,所以twisted的代码必须可以调用我们的定义的函数,我们让callwhenrunning 传递一个hello函数的引用到twisted code,并触发callwhenrunning.但让必须在ractor运行之后才可以.
我们用术语callback来描述这种引用, 一个callback 是我们给twisted的一个函数的引用,twisted 会call back 在正确的时候.既然twisted 和我们的业务逻辑是分开的,reactor 和我们的业务逻辑通过callback一个函数来进行交互.
我们可以看到下面的程序看到twisted怎样和我们的程序交互的:
import traceback
def stack():
print 'The python stack:'
traceback.print_stack()
from twisted.internet import reactor
reactor.callWhenRunning(stack)
reactor.run()
你可以在basic-twisted/stack.py看到源代码,会输出一些像下面的东西:
The python stack:
...
reactor.run()
...
...
...
traceback.print_stack()
不要在意中间的一些twisted之间的调用,注意reactor.run() 和我们的callback之间的关系
callback 是做什么的
twisted 不是唯一一个用callback 的reactor 框架, 其他的异步框架Medusa和asyncore也在用,还有一个GUI 比如GTK和QT,也是基于reactor循环的.
"反应器"系统的开发者们非常爱callback,但是要考虑到下面几点:
反应堆模式是单线程的
一个reactor的框架比如twisted实现了reactor循环所以我们的代码不要再去实现了
我们的代码仍旧需要被调用来实现我们的业务逻辑
既然整个程序在单线程内,reactor循环仍需要调用我们的代码
reactor不能事先知道我们哪部分代码需要被调用
考虑到上面几点,callback就是必须的了
图片六描述了callback 的时候发生了什么
图片六
图片六说明一些callback 的重要属性:
我们的callback 代码和twisted 循环运行在同一个线程中
当我们的callback在运行的时候,twisted 循环不运行
同上,当twisted 循环运行时,callback是不运行的
reactor循环恢复,当我们的callback返回时
在一个callback中,twisted 循环可以看作是被阻塞住了.所以我们应该确保我们的callback代码不要花费很多时间.通常的,我们应该在我们的callback中避免阻塞的I/O操作.否则我们使用反应器模式的好处就不存在了.twisted 不会提供任何防范方法去阻止你的阻塞程序,我们必须确保在callback中不要做阻塞操作.不过最后你会发现,我们不用担心基本的网络I/O操作,因为我们可以让twisted去为我们实现异步通信.
其他的可能出现阻塞的操作包括从不是socket 文件描述符(比如pipe)中读写,或者等待一个子进程结束.怎样把阻塞的操作编程的非阻塞的操作跟你要完成什么有很大的关系,但是总会有twisted api 可以帮助实现它.记住,一些标准的python 函数本身就是阻塞模式的,没有方法转为非阻塞模式,比如,os.system 函数会阻塞直到子进程运行完成,他们就是那样工作的,所以在你用twisted的时候,你应该避免在你的代码中使用这些阻塞函数. Goodbye Twisted
你可以让twisted 的reactor 停止运行通过 reactor 的stop方法.但是一但reactor 被停止它不能被重启,所以在你的程序不得不停止的时候再去调用reactor 的stop方法. 曾经在邮件列表中讨论过让reactor变为可重启动的,你可以控制reactor 什么时候开始,什么时候停止.在twisted 8.2.0中,你仅仅可以启动reactor一次
下面有一个程序,basic-twisted/countdown.py,在5秒后停止reactor:
class Countdown(object):
counter = 5
def count(self):
if self.counter == 0:
reactor.stop()
else:
print self.counter, '...'
self.counter -= 1
reactor.callLater(1, self.count)
from twisted.internet import reactor
reactor.callWhenRunning(Countdown().count)
print 'Start!'
reactor.run()
print 'Stop!'
这个程序用callLater API 注册了一个callback,这个callback作为callLater 的第二参数,第一个参数是等待多少秒然后你的callback才被执行.
twisted 是怎样在正确的实行去执行callback 的?既然这个程序没有监听任何的文件描述符,为什么也会在select循环中被卡住呢? 事实是,select 还有其他的类似的poll 也接收一个可选的timeout值,如果在一个timeout 的时间周期内没有任何一个文件描述符可以读写,select 也会返回.如果你传递给timeout 一个0值,你可以没有阻塞的监听文件描述符.
你可以把timeout 想象成一个事件循环正在等的事件(参照图片五),twisted 用timeout 来确保任何一个被定时了的callback可以在正确的时间运行.假如一个callback需要花费很长的时间,一个定时的callback会被顺延,所以twisted 的callLater 机制不能提供非常准确的时间保证.
下面是这个程序的输出
Start!
5 ...
4 ...
3 ...
2 ...
1 ...
Stop!
最后会输出一个stop表示reactor 已经退出了,现在有了一个可以自己的停止的程序了.
接招吧 (让我想起了take that 乐队)twisted
既然twisted 以callback的方法来执行我们的方法,你会想如果一个callback抛出了异常怎么办.让我们试试吧,basic-twisted/exception.py会在一个callback中抛出一个异常:
def falldown():
raise Exception('I fall down.')
def upagain():
print 'But I get up again.'
reactor.stop()
from twisted.internet import reactor
reactor.callWhenRunning(falldown)
reactor.callWhenRunning(upagain)
print 'Starting the reactor.'
reactor.run()
你会看到以下输出:
Starting the reactor.
Traceback (most recent call last):
... # I removed most of the traceback
exceptions.Exception: I fall down.
But I get up again.
注意第二个callback仍会在第一个callback之后运行,即使我们看到了很多的异常的追踪信息.如果你把reactor.stop()注释掉的话,这个程序会仍会继续运行下去,所以reactor 会继续运行下去即使我们的一个callback抛出了异常,
网络服务器对鲁棒性要求比较高,即使在运行过程中出现了一些bug也不能让服务器down掉,不是在说我们不用去处理我们的错误,如果你感觉后面还有人支持着你,你会感觉很棒.
Poetry,please
下面我们要twisted去获取点小诗啦,在第四部分,我们会实现一个twised 的异步的客户端
-
twisted例子
2017-12-05 20:00:43这个代码例子要结合之前上传的《Twisted系列教程等》这个一起看。 -
Twisted support
2020-11-25 11:29:14ve now been asked twice if I can port <code>hyper</code> to Twisted. Should be a matter of writing a <code>Connection</code> object for Twisted. <p>Might be do-able, certainly worth looking into.</p>... -
python Twisted
2017-06-29 09:47:50windows下的Twisted-17.5.0其中包括cp27,cp27m,cp34,cp34m,cp35,cp35m,cp36,cp36m,使用平台包括win_amd64,win32 -
python twisted教程_Python Twisted系列教程3:初步认识Twisted
2020-12-06 14:10:15用twisted的方式实现前面的内容最终我们将使用twisted的方式来重新实现我们前面的异步模式客户端。不过,首先我们先稍微写点简单的twisted程序来认识一下twisted。最最简单的twisted程序就是下面的代码,其在twisted...可以从这里从头开始阅读这个系列。
用twisted的方式实现前面的内容
最终我们将使用twisted的方式来重新实现我们前面的异步模式客户端。不过,首先我们先稍微写点简单的twisted程序来认识一下twisted。
最最简单的twisted程序就是下面的代码,其在twisted-intro目录中的中。
from twisted.internet import reactor
reactor.run()
可以用下面的命令来运行它:
python basic-twisted/simple.py
正如在第二部分所说的那样,twisted是实现了Reactor模式的,因此它必然会有一个对象来代表这个reactor或者说是事件循环,而这正是twisted的核心。上面代码的第一行引入了reactor,第二行开始启动事件循环。
这个程序什么事情也不做。除非你通过ctrl+c来终止它,否则它会一直运行下去。正常情况下,我们需要给出事件循环或者文件描述符来监视I/O(连接到某个服务器上,比如说我们那个诗歌服务器)。后面我们会来介绍这部分内容,现在这里的reactor被卡住了。值得注意的是,这里并不是一个在不停运行的简单循环。如果你在桌面上有个CPU性能查看器,可以发现这个循环体不会带来任何性能损失。实际上,这个reactor被卡住在第二部分图5的最顶端,等待永远不会到来的事件发生(更具体点说是一个调用select函数,却没有监视任何文件描述符)。
下面我们会让这个程序丰富起来,不过事先要说几个结论:
Twisted的reactor只有通过调用reactor.run()来启动。
reactor循环是在其开始的进程中运行,也就是运行在主进程中。
一旦启动,就会一直运行下去。reactor就会在程序的控制下(或者具体在一个启动它的线程的控制下)。
reactor循环并不会消耗任何CPU的资源。
并不需要显式的创建reactor,只需要引入就OK了。
最后一条需要解释清楚。在Twisted中,reactor是Singleton(也是一种模式),即在一个程序中只能有一个reactor,并且只要你引入它就相应地创建一个。上面引入的方式这是twisted默认使用的方法,当然了,twisted还有其它可以引入reactor的方法。例如,可以使用来代替select方法。
若使用其它的reactor,需要在引入twisted.internet.reactor前安装它。下面是安装pollreactor的方法:
from twisted.internet import pollreactor
pollreactor.install()
如果你没有安装其它特殊的reactor而引入了twisted.internet.reactor,那么Twisted会为你安装selectreactor。正因为如此,习惯性做法不要在最顶层的模块内引入reactor以避免安装默认reactor,而是在你要使用reactor的区域内安装。
下面是使用 pollreactor重写上上面的程序,可以在中找到:
from twited.internet import pollreactor
pollreactor.install()
from twisted.internet import reactor
reactor.run()
上面这段代码同样没有做任何事情。
后面我们都会只使用默认的reactor,就单纯为了学习来说 ,所有的不同的reactor做的事情都一样。
你好,Twisted
我们得用Twisted来做什么吧。下面这段代码在reactor循环开始后向终端打印一条消息:
def hello():
print 'Hello from the reactor loop!'
print 'Lately I feel like I\'m stuck in a rut.'
from twisted.internet import reactor
reactor.callWhenRunning(hello)
print 'Starting the reactor.'
reactor.run()
这段代码可以在中找到。运行它,会得到如下结果:
Starting the reactor.
Hello from the reactor loop!
Lately I feel like I'm stuck in a rut.
仍然需要你手动来关掉程序,因为它在打印完毕后就又卡住了。
值得注意的是,hello函数是在reactor启动后被调用的。这意味是reactor调用的它,也就是说Twisted在调用我们的函数。我们通过调用reactor的callWhenRunning函数,并传给它一个我们想调用函数的引用来实现hello函数的调用。当然,我们必须在启动reactor之前完成这些工作。
我们使用回调来描述hello函数的引用。回调实际上就是交给Twisted(或者其它框架)的一个函数引用,这样Twisted会在合适的时间调用这个函数引用指向的函数,具体到这个程序中,是在reactor启动的时候调用。由于Twisted循环是独立于我们的代码,我们的业务代码与reactor核心代码的绝大多数交互都是通过使用Twisted的APIs回调我们的业务函数来实现的。
我们可以通过下面这段代码来观察Twisted是如何调用我们代码的:
import traceback
def stack():
print 'The python stack:'
traceback.print_stack()
from twisted.internet import reactor
reactor.callWhenRunning(stack)
reactor.run()
The python stack:
... reactor.run()
... ...
traceback.print_stack()
不用考虑这其中的若干Twisted本身的函数。只需要关心reactor.run()与我们自己的函数调用之间的关系即可。
有关回调的一些其它说明:
Twisted并不是唯一使用回调的框架。许多历史悠久的框架都已在使用它。诸多GUI的框架也是基于回调来实现的,如GTK和QT。
交互式程序的编程人员特别喜欢回调。也许喜欢到想嫁给它。也许已经这样做了。但下面这几点值得我们仔细考虑下:
reactor模式是单线程的。
像Twisted这种交互式模型已经实现了reactor循环,意味无需我们亲自去实现它。
我们仍然需要框架来调用我们自己的代码来完成业务逻辑。
因为在单线程中运行,要想跑我们自己的代码,必须在reactor循环中调用它们。
reactor事先并不知道调用我们代码的哪个函数这样的话,回调并不仅仅是一个可选项,而是游戏规则的一部分。
图6说明了回调过程中发生的一切:
图6 reactor启用回调
图6揭示了回调中的几个重要特性:
我们的代码与Twisted代码运行在同一个进程中。
当我们的代码运行时,Twisted代码是处于暂停状态的。
同样,当Twisted代码处于运行状态时,我们的代码处于暂停状态。
reactor事件循环会在我们的回调函数返回后恢复运行。
在一个回调函数执行过程中,实际上Twisted的循环是被有效地阻塞在我们的代码上的。因此,因此我们应该确保回调函数不要浪费时间(尽快返回)。特别需要强调的是,我们应该尽量避免在回调函数中使用会阻塞I/O的函数。否则,我们将失去所有使用reactor所带来的优势。Twisted是不会采取特殊的预防措施来防止我们使用可阻塞的代码的,这需要我们自己来确保上面的情况不会发生。正如我们实际所看到的一样,对于普通网络I/O的例子,由于我们让Twisted替我们完成了异步通信,因此我们无需担心上面的事情发生。
其它也可能会产生阻塞的操作是读或写一个非socket文件描述符(如管道)或者是等待一个子进程完成。
如何从阻塞转换到非阻塞操作取决你具体的操作是什么,但是也有一些Twisted APIs会帮助你实现转换。值得注意的是,很多标准的Python方法没有办法转换为非阻塞方式。例如,os.system中的很多方法会在子进程完成前一直处于阻塞状态。这也就是它工作的方式。所以当你使用Twisted时,避开使用os.system。
退出Twisted
原来我们可以使用reactor的stop方法来停止Twisted的reactor。但是一旦reactor停止就无法再启动了。(Dave的意思是,停止就退出程序了),因此只有在你想退出程序时才执行这个操作。
class Countdown(object):
counter = 5
def count(self):
if self.counter == 0:
reactor.stop()
else:
print self.counter, '...'
self.counter -= 1
reactor.callLater(1, self.count)
from twisted.internet import reactor
reactor.callWhenRunning(Countdown().count)
print 'Start!'
reactor.run()
print 'Stop!'
在这个程序中使用了callLater函数为Twisted注册了一个回调函数。callLater中的第二个参数是回调函数,第一个则是说明你希望在将来几秒钟时执行你的回调函数。那Twisted如何来在指定的时间执行我们安排好的的回调函数。由于程序并没有监听任何文件描述符,为什么它没有像前那些程序那样卡在select循环上?select函数,或者其它类似的函数,同样会接纳一个超时参数。如果在只提供一个超时参数值并且没有可供I/O操作的文件描述符而超时时间到时,select函数同样会返回。因此,如果设置一个0的超时参数,那么会无任何阻塞地立即检查所有的文件描述符集。
你可以将超时作为图5中循环等待中的一种事件来看待。并且Twisted使用超时事件来确保那些通过callLater函数注册的延时回调在指定的时间执行。或者更确切的说,在指定时间的前后会执行。如果一个回调函数执行时间过长,那么下面的延时回调函数可能会被相应的后延执行。Twisted的callLater机制并不为硬实时系统提供任何时间上的保证。
下面是上面程序的输出:
Start!
5 ...
4 ...
3 ...
2 ...
1 ...
Stop!
捕获它,Twisted
由于Twisted经常会在回调中结束调用我们的代码,因此你可能会想,如果我们的回调函数中出现异常会发生什么状况。(Dave的意思是说,在结束我们的回调函数后会再次回到Twisted代码中,若在我们的回调中发生异常,那是不是异常会跑到Twisted代码中,而造成不可想象的后果 )让我们来试试,在中的程序会在一个回调函数中引发一个异常,但是这不会影响下一个回调:
def falldown():
raise Exception('I fall down.')
def upagain():
print 'But I get up again.'
reactor.stop()
from twisted.internet import reactor
reactor.callWhenRunning(falldown)
reactor.callWhenRunning(upagain)
print 'Starting the reactor.'
reactor.run()
当你在命令行中运时,会有如下的输出:
Starting the reactor. Traceback (most recent call last):
... # I removed most of the traceback
exceptions.Exception: I fall down.
But I get up again.
注意,尽管我们看到了因第一个回调函数引发异常而出现的跟踪栈,第二个回调函数依然能够执行。如果你将reactor.stop()注释掉的话,程序会继续运行下去。所以说,reactor并不会因为回调函数中出现失败(虽然它会报告异常)而停止运行。
网络服务器通常需要这种健壮的软件。它们通常不希望由于一个随机的Bug导致崩溃。也并不是说当我们发现自己的程序内部有问题时,就垂头丧气。只是想说Twisted能够很好的从失败的回调中返回并继续执行。
请继续讲解诗歌服务器
现在,我们已经准备好利用Twisted来搭建我们的诗歌服务器。在第4部分,我们会实现我们的异步模式的诗歌服务器的Twisted版。
-
twisted python_twisted系列教程十一—一个twisted 的服务端
2020-11-25 17:06:44原文:http://krondo.com/blog/?p=1209 Chinese by Yang Xiaowei and Cheng Luo A Twisted Poetry Server既然我们已经学了这么多twisted client 的编写,现在让我们来用twisted来重新实现一下我们的poetry server 吧.... -
Twisted.rar
2020-08-30 19:12:15解决方法:先下载安装Twisted(用pip无法安装),然后再用pip安装scrapy即可。 国外镜像下载速度慢,甚至网页都无法打开,于是我下载了多个版本打包,供有需要的朋友使用: Twisted‑20.3.0‑cp39‑cp39‑win_amd64.... -
twisted_资料twisted_资料
2010-09-16 23:57:23twisted_资料twisted_资料twisted_资料 -
pip安装提示Twisted错误问题(Python3.6.4安装Twisted错误)
2020-12-20 08:18:20当我们在安装scrapy的过程中出现了Twisted错误,当我们有继续安装Twisted的时候,又继续报错,通过一系列的查询和了解,终于发现了问题,现在就来和大家一起解决这个复杂的BUG…… 环境 Python3.6.4 + Windows 10 ...