设为首页 收藏本站
查看: 815|回复: 0

[经验分享] Twisted:Python事件驱动的网络引擎库

[复制链接]

尚未签到

发表于 2015-12-1 14:25:30 | 显示全部楼层 |阅读模式


介绍
  twisted 是python下一个事件驱动的网络引擎库, 支持很多种的协议.
它包含了一个web服务, 多种IM客户端,服务端, 邮件服务协议. 由于规模庞大, twisted分成了几个sub-project. 一起或者分开发布.

稳定性
  twisted并不是从0.1发展到8.1.0的. 是一下子跳到8的. 8.1.0是最新的stable的包.
从successful stories来看, 它的客户包括NASA这种级别的(http://twistedmatrix.com/trac/wiki/SuccessStories#NASA).

效率
  twisted具有惊人的效率.

入门教程
  Twisted应用的基本问题,可说是“一个中心,两个基本点”,即:
以“事件”event为中心,以"建立连接"connect和“定义反馈“callback为基本点。
在这些问题上,Twisted有一整套固定的路数,只能照章行事,没有自由发挥的余地。前面把twisted的套路概括成一句话,“一个中心,两个基本点”,现在就从这个“中心”聊起。
Twisted 官方说,“ Twisted is an event-driven networking framework
”。事实的确如此。从其运行机制上看,event 是 Twisted
运转的引擎,是发生各种动作的启动器,是牵一发而动全身的核心部件。从其架构组成上看,它是紧密围绕event设计的;它的具体应用
application,主要是定义、实现各式各样的event,由此完成不同网络协议的连接和输入输出任务,满足用户的实际需求;从其
application的文本形式上,可以直接看到,它的应用程序,基本上由一系列event构成。
由此可见,说它以event为中心,符合实际情况。
Twisted 对event 的管理机制,可划分为后台和前台两种形式。
后台的管理,是Twisted 框架的内在机制,自动运行,对程序员透明无须干预,在程序文本中不见其踪迹。
前台的管理,是Twisted 授权程序员,在程序文本中显式写码来实现。程序员的工作,主要是按照既定的方式,实现 event。我们所关心、所用到的,是这部分东西(API)。
Twisted 众多的 event,分门别类、层次有序。前台管理中,有两个特别的 object,一个叫 reactor ,另一个叫deferred。特别之处,在于它俩起着“事件管理器”的作用。下面,说说它俩。

一、统领全局的 reactor
  在 Twisted 应用中,reactor 的任务是为程序运行建立必须的全局循环(event loop),所起的作用,相当于 Python 应用中的 MainLoop()。
reactor 的用法很简单,一般只用两个:reactor.run() 启动全局循环,reactor.stop() 停止全局循环(程序终止)。
如果程序中没有调用reactor.stop() 的语句,程序将处于死循环,可以按键 Ctrl-C 强制退出。
下面是一个例子:









1 import time
2 from twisted.internet import reactor
3
4 def printTime( ):
5     print "Current time is", time.strftime("%H:%M:%S")
6  
7 def stopReactor( ):
8     print "Stopping reactor"
9     reactor.stop( )
10  
11 reactor.callLater(1, printTime)
12 #定时器,1秒钟后调用printTime()
13 reactor.callLater(2, printTime)
14 reactor.callLater(3, printTime)
15 reactor.callLater(5, stopReactor)
16 #定时器,5秒钟后调用stopReactor()
17 print "Running the reactor..."
18 reactor.run( )
19 print "Reactor stopped."
  
  

二、提升效率的 deferred
  
Twisted 官方称,“Twisted is event-based, asynchronous framework ”。这个“异步”功能的代表就是 deferred。
deferred 的作用类似于“多线程”,负责保障多头连接、多项任务的异步执行。
当然,deferred “异步”功能的实现,与多线程完全不同,具有以下特点:

1、deferred 产生的 event,是函数调用返回的对象;

2、deferred 代表一个连接任务,负责报告任务执行的延迟情况和最终结果;

3、对deferred 的操作,通过预定的“事件响应器”(event handler)进行。
有了deferred,即可对任务的执行进行管理控制。防止程序的运行,由于等待某项任务的完成而陷入阻塞停滞,提高整体运行的效率。
请看下面的例子:
建议只关注黑体字的语句,它们反映了deferred的用法。涉及的两个class,是Twisted建立网络连接的固定套路,后面会专门说它。






01.# connectiontest.py

02.from twisted.internet import reactor, defer, protocol

03.

04.class CallbackAndDisconnectProtocol(protocol.Protocol):

05.# Twisted建立网络连接的固定套路

07.  def connectionMade(self):

08.    self.factory.deferred.callback("Connected!")

09.    # “事件响应器”handleSuccess对此事件作出处理

11.    self.transport.loseConnection( )

12.

13.

14.class ConnectionTestFactory(protocol.ClientFactory):

15.# Twisted建立网络连接的固定套路

17.  protocol = CallbackAndDisconnectProtocol

19.  def __init__(self):

20.    self.deferred = defer.Deferred( )

21.    # 报告发生了延迟事件,防止程序阻塞在这个任务上

22.

23.  def clientConnectionFailed(self, connector, reason):

25.    self.deferred.errback(reason)

26.    # “事件响应器”handleFailure对此事件作出处理

27.

28.def testConnect(host, port):

29.  testFactory = ConnectionTestFactory()

30.  reactor.connectTCP(host, port, testFactory)

31.

32.  return testFactory.deferred

33.  # 返回连接任务的deferred

34.

35.def handleSuccess(result, port):

36.# deferred“事件响应器”:连接任务完成的处理

38.  print "Connected to port %i" % port

39.  reactor.stop()

40.

42.def handleFailure(failure, port):

43.# deferred“事件响应器”:连接任务失败的处理

45.  print "Error connecting to port %i: %s" % (port, failure.getErrorMessage())

46.  reactor.stop( )

47.

48.

49.if __name__ == "__main__":

51.  import sys

52.

53.  if not len(sys.argv) == 3:

54.      print "Usage: connectiontest.py host port"

55.      sys.exit(1)

56.

58.  host = sys.argv[1]

59.  port = int(sys.argv[2])

60.

61.  connecting = testConnect(host, port)

62.  # 调用函数,返回deferred

63.

64.  connecting.addCallback(handleSuccess, port)

65.  # 建立deferred“事件响应器”

66.

67.  connecting.addErrback(handleFailure, port)

68.  # 建立deferred“事件响应器”

69.

70.  reactor.run( )  

三、创建 client 的套路
  
第二节说到的两个类,是TCP协议客户端的创建套路(方式)。这个套路拆解如下:
1、定义“工厂”和“协议”两个类:
    (1)“协议”类是 CallbackAndDisconnectProtocol,“工厂”类是 ConnectionTestFactory
类的名字不重要,但必须正确说明所继承的父类:
class CallbackAndDisconnectProtocol(protocol.Protocol)
class ConnectionTestFactory(protocol.ClientFactory)
    (2)“协议”类是“工厂”类实例化的:protocol = CallbackAndDisconnectProtocol;
    (3)只在“工厂”类中有 __init__ 函数,并在其中实例化一个deferred 对象:
self.deferred = defer.Deferred( )
    (4)在“工厂”类中,重设父类函数 clientConnectionFailed,通过deferred 引发事件,报告连接失败:
self.deferred.errback(reason)     
    (5)在“协议”类中,重设父类函数 connectionMade,由对象factory引用“工厂”类中的deferred,经其引发事件,报告连接正常:
self.factory.deferred.callback("Connected!")
并由对象transport引发事件,报告连接断开:
self.transport.loseConnection( );
上述“对象”,都是从各自父类继承来的。

2、在函数testConnect(host, port)中,
    (1)将“工厂”类实例化:testFactory = ConnectionTestFactory( )
    (2)由全局循环“主持人”reactor建立以testFactory为“主演”的TCP连接:
reactor.connectTCP(host, port, testFactory)
    (3)返回deferred对象:return testFactory.deferred

至此,一个以事件驱动为基础、异步执行任务的框架程序搭成了。
上述三节的内容,据 Twisted 官方说,是“学习曲线最陡”的部分(They represent the steepest part of the Twisted learning curve.)。
我的感受,造成“最陡”的原因,是由于套路新颖独特,初学乍练不易适应。

1、框架对象众多,一时记不牢;

2、对象之间的关系比较复杂,一时理不清;

3、“事件驱动”这种模式,反映在程序文本中,有时见不到明显的函数调用,让人觉得程序的去向不明;

另外,学习方法很重要。如果以学“语言”的习惯来学框架,遇上问题寻根究底,过分追求“水落石出”;或者,依赖教科书、畸重“理论”,忽视 examples 语句、结构和API文档的分析研究,都不利于翻越这段陡坡。



据我的体验,集中精力地啃嚼主干骨架,不纠缠于细枝末节,这段最陡的上坡路,顶多爬个十天八天的,就能越过去。


  

四、创建 server 的套路
  
网络程序,总得传送数据什么的。本节说说这事儿,一个玩具式的对话服务器。
咱这儿说事儿,还是老套路,用例程说话。
我是这样想的:框架这玩艺儿,是让咱比着葫芦画瓢使的,不必太在意葫芦为啥长成那般模样。自己试着例程跑得起来,自然这瓢是画对了,也不用管那模特葫芦的内瓤是啥名堂了。当然,尽量把葫芦内瓤搞清楚更好,画起瓢来心里更有底、更塌实吧。言归正传,画瓢开始。



1 from twisted.internet import reactor, protocol
2  
3 class Talk(protocol.Protocol):
4  
5   def dataReceived(self, data):
6     print "Client:",data
7     if not data=='bye':
8       s=''
9       while(s==''):
10         s=raw_input('Server: ')
11         s=s.strip()
12       self.transport.write(s)
13  
14 def main():
15   factory = protocol.ServerFactory()
16   factory.protocol = Talk
17   reactor.listenTCP(8000,factory)
18   reactor.run()
19  
20 if __name__ == '__main__':
21   main()
  
先来说说咱这“瓢”。
1、服务器的构建启动:从调用函数 main() 开始,生成“工厂”对象factory,指定factory用的通讯“协议”为Talk,指定以端口8000和factory构成服务器,并启动它。
2、服务器的运行:类Talk中的方法dataReceived,实际上是个event,当收到客户端传来的数据时,它做这几件事儿:
     (1)在显示器上写出:Client:和传来的数据;
     (2)如果传来的数据是字符串“bye”,不作处理。否则,屏显提示“Server:",要求键入一行字符;如果键入的不全是空格,将其传给客户端。
再来看看“葫芦的内瓤”。
1、为什么在程序中,没有“工厂”类。在protocol.py中定义的类ServerFactory是个空类,没有“方法”可以重设
(注:protocol.py中各类的“方法”,基本上都是“事件”);其父类Factory中虽有方法可供重设,但本程序太简单用不着。

在类Factory中,只有以下3个方法可以在程序文本中重设:
       (1)buildProtocol(self, addr),用以改变“工厂”类所用Protocal的创建方式;
       (2)startFactory(self),在factory开始监听连接前,仅调用一次。用于连接数据库、打开文件等操作;
       (3)stopFactory(self),用于关闭数据库、文件等操作。
       可否在程序文本中显式调用以上3个方法,protocol.py注释里明确说,不允许调用stopFactory。但从逻辑上,它们作为“事件”,应该只能重设不许直接调用。
2、类Protocol的简单介绍
       它有2个方法用以重设:
       (1)dataReceived(self, data),当收到客户端传来的数据时,执行它。data是不定长字符串;
       (2)connectionLost(self, reason=connectionDone),当连接断开时,执行它;reason的类型是: L{twisted.python.failure.Failure}
3、类Protocol父类 BaseProtocol 的简单介绍
       BaseProtocol 是各种网络协议的抽象父类。如果实现一个新协议加入Twisted,不管是客户端还是服务器端的协议,都应该以 BaseProtocol作为父类。
       BaseProtocol 的API很简单。在子类 Protocol 中实现了方法 dataReceived(data)
,处理基于事件和异步的输入;输出则由属性对象 'transport'
传送。
  'transport' L{twisted.internet.interfaces.ITransport}。定义了二个方法,其中,可重设的是 connectionMade(self),当连接完成时,执行它。可以将其看作”协议“类的初始化方法。

下面是客户端。它已是脸熟的老朋友了,但没用deferred,其他不用多说了吧。









1 from twisted.internet import reactor, protocol
2  
3 class TalkClient(protocol.Protocol):
4   def connectionMade(self):
5     s=self.say()
6     self.transport.write(s)
7  
8   def dataReceived(self, data):
9     print "Server:", data
10     s=self.say()
11     self.transport.write(s)
12     if s=='bye':
13       self.transport.loseConnection()
14  
15   def connectionLost(self, reason):
16     print "connection lost"
17     from twisted.internet import reactor
18     reactor.stop()
19  
20   def say(self):
21     s=''
22     while(s==''):
23       s=raw_input('Client: ')
24       s=s.strip()
25     return s
26  
27 class TalkFactory(protocol.ClientFactory):
28   protocol = TalkClient
29  
30   def clientConnectionFailed(self, connector, reason):
31     print "Connection failed - goodbye!"
32     reactor.stop()
33  
34   def clientConnectionLost(self, connector, reason):
35     print "Connection lost - goodbye!"
36     reactor.stop()
37  
38  
39 def main():
40   f = TalkFactory()
41   reactor.connectTCP("localhost", 8000, f)
42   reactor.run()
43  
44 if __name__ == '__main__':
45  
46   f = TalkFactory()
47   reactor.connectTCP("localhost", 8000, f)
48   reactor.run()
  




参考资料:

1、twisted异步机制--Deferred   


2、Twisted异步编程--Deferred  


3、learning twisted -- stackoverflow

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-145950-1-1.html 上篇帖子: 23 其它话题 下篇帖子: Python列表去重
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表