twisted系列教程六–继续重构twisted poetry client
Poetry for Everyone
我们已经在我们的client取得了很大的进步,我们的2.0版本已经试用了Transports,Protocols 和Protocols Factories.但是仍有很多可以提升的地方.2.0 版本的client版本仅仅可以在命令行下载诗.这是因为PoetryClientFactory 不仅仅负责下载诗,也负责在下载完的时候停掉这个程序.这对一个Protocol Factory类来说太奇怪了,它应该只用来创建PoetryProtocols 和 收集已经运行下载完的诗.
我们需要一种可以把这首诗交给我们代码的方法,但是你必须先获取这首诗,在一个同步的程序中我们可以这样做:
def get_poetry(host, post):
"""Return a poem from the poetry server at the given host and port."""
但是当然的,我们不能在这里这样做.上面的代码会阻塞直到这首诗被完全的接收,否则的话它不会像它注释中说明的一样.但是这是一个reactive 程序,网络阻塞可以被它很好的解决.我们需要一个可以在诗下载完的时候通知我们的代码,以及在下载时不阻塞的方法.这个问题就像twisted 遇到的问题一样,twisted 需要在一个socket 可以进行I/O 的时候告诉我们.twisted 用callback 的方法很好的解决了这个问题,所以我们也可以这样用:
def get_poetry(host, port, callback):
"""
Download a poem from the given host and port and invoke
callback(poem)
when the poem is complete.
"""
现在我们有了一个可以让twisted 使用的api 了,让我们继续.
就像我以前说的,我们有时会不按照twisted 的方式的写代码,上面的写法就不是twisted 的写法,我们会在第七部分和八部分用twisted 的方式来改写它.用最简单的方式开始写代码可以让我们更深入的理解.
Client 3.0
你可以看到我们的poety client 3.0 版本在twisted-client-3/get-poetry.py,这个版本有一个get_poetry 方法的实现:
def get_poetry(host, port, callback):
from twisted.internet import reactor
factory = PoetryClientFactory(callback)
reactor.connectTCP(host, port, factory)
需要注意的是我们传递callback 给PoetryClientFactory,factory 用这个callback传递诗:
class PoetryClientFactory(ClientFactory):
protocol = PoetryProtocol
def __init__(self, callback):
self.callback = callback
def poem_finished(self, poem):
self.callback(poem)
现在的factory 比client 2.1 版本的简单多了,因为factory 不用再去关心停掉reactor 了,也少了捕捉错误的代码,我们一会会加上的.而 PoetryProtocol 则不需要做任何改变,我们可以重用它.:
class PoetryProtocol(Protocol):
poem = ''
def dataReceived(self, data):
self.poem += data
def connectionLost(self, reason):
self.poemReceived(self.poem)
def poemReceived(self, poem):
self.factory.poem_finished(poem)
在这些改变之后,get_poetry,PoetryClientFactory,PoetryProtocol都可以完全重用了.它们都只负责下载诗.所有的初始化,关闭reactor 的逻辑代码全部在我们的主函数poetry_main:
def poetry_main():
addresses = parse_args()
from twisted.internet import reactor
poems = []
def got_poem(poem):
poems.append(poem)
if len(poems) == len(addresses):
reactor.stop()
for address in addresses:
host, port = address
get_poetry(host, port, got_poem)
reactor.run()
for poem in poems:
print poem
我们可以把这些可以重用的部分都放到一个模块中,然后任何人都可以获取诗了.^_^.
顺便说一下,在你实际的测试client 3.0 的时候,你可以重新配置一下poetry server 让它一次多输出一些数据块.
Discussion
我们可以把诗传递的过程用图片十一 形象化:
图片十一
图片十一是值得多想想的,到现在为止我们描述的callback 链是以我们自己写的代码终止的.但是当你用twisted或者其他的reactive 系统 写程序的时候,我们的callback 链会出现一小段代码callback另一小段代码的情况.也就是说,reactive 类型的程序在到达我们写的代码的时候不会停止,它会不断callback 下去.
在你选择twisted 的时候,请把下面的话记到你的心里.当你做了这个决定之后:
我要用twisted了啦啦啦
你也要做这个决定:
我将要把我的程序构造成由reactor触发的一系列的callback
也许你现在不会大声地将它说出来,但twisted 就是这样的.twisted 就是这样工作的.可能大多数的python程序是同步的而且大多数的python 模块也是同步的.假如我们正在写同步(原文这里写的是同步,我怀疑有点问题,应该是异步)的程序然后忽然意识到我们需要获取诗,我们可以使用get_poetry 函数,就像下面的写法:
...
import poetrylib # I just made this module name up
poem = poetrylib.get_poetry(host, port)
...
然后我们继续,假如不久以后我们根本不需要诗,然后就可以删除上面的两行,对所有的程序都不会造成什么影响.但是假如我们正在写一个同步的程序然后决定用twisted 版本的额get_poetry,我们就需要用callbacks 来重构我们的程序.我们可能会对代码改动很多.我并不是说重写代码是一个错误,根据我们的需求去重构代码是很有意义的.但它不会只增加几行代码那么简单.简单来讲,同步的和异步的程序不能混合在一起.
如果你对twisted 和异步编程了解不是很多,我还是建议你开始研究大型的twisted 程序代码库之前自己先实现几个简单的demo. 这样的话你会在没有其他复杂的干扰下找到twisted 的感觉.假如你的程序已经是异步的,和twisted结合起来就会相对简单.twisted 和 pyGTK 和 pyQT 就结合的很好.
When Things Go Wrong
在client 3.0 版本中我们不再监测当连接服务器时出现的错误,就像在client 1.0 版本中的那样.假如我们让client 3.0 从一个不存在的server上下载诗的话,client 3.0 不会崩溃掉而是在原地不停的等待.clientConnectionFailed callback 仍旧会被调用,但是ClientFactory中的默认clientConnectionFailed 什么也不做,所以got_poem callback 永远不会被调用,reactor 永远不会停,然后我们又成功的写了另一个什么也不做的程序.
很明显的我们需要来处理这个错误,但是在哪里呢?错误信息通过clientConnectionFailed 被传递到factory,所以我们从这里开始,但是这个factory 应该是可用的,正常的处理错误的方法应该依据factory被调用的地方的上下文来处理.在一些程序中,接收不到诗歌可能会是一个灾难,在令一些程序中,我们仍旧可以继续运行(这里是在说twisted 的容错性比较强).
换句话说,当你用get_poetry 的时候需要知道什么时候会出错,不仅仅是什么时候是对的.在一个同步的系统中,get_poetry 会抛出一个异常,然后用一个try/except 进行捕捉,但是在一个reactive 的系统里,错误信息也必须以异步的方式传递.毕竟我们直到get_poetry 返回的时候我们才能发现连接错误了,下面是一种可能的情况:
def get_poetry(host, port, callback):
"""
Download a poem from the given host and port and invoke
callback(poem)
when the poem is complete. If there is a failure, invoke:
callback(None)
instead.
"""
通过监测callback 的参数,client可以确定是否我们最终得到了一首诗.这样就可以防止我们的程序永远运行下去,但是还是会有一些小问题,当你向callback传递None的时候,并不能概括到所有的出错信息,而且twisted 的一些api 也会默认的返回None,所以这里我们要用err 参数来替代None,err中可以包含具体的出错信息.就像下面的一样:
def get_poetry(host, port, callback):
"""
Download a poem from the given host and port and invoke
callback(poem)
when the poem is complete. If there is a failure, invoke:
callback(err)
instead, where err is an Exception instance.
"""
如果这里用一个异常就基本上和我们的同步程序一样了.现在我们可以从异常中获取出错的信息.正常的,在我们在平常的python代码中如果遇到了异常我们会输出traceback供我们调试用.
请记住在我们的callback被触发的时候我们并不想要一个traceback.我们真正想要的是在出现异常的地方的exception实例 和 当时的traceback.
twisted 包含了一个叫做Failure 的抽象,failure是Exception 和 traceback 的封装.Failure 文档描述了怎样创建一个failure.通过传递给callback一个Failure对象,我们可以很好的保护traceback 信息.
在 twisted-failure/failure-examples.py中有一些Failure 对象的用法,它演示了Failure 是怎样保护traceback信息的,即使在一个except 代码块上下文之外.我们现在不会在怎样建立Failure 实例上花费很多时间,在第七部分,我们会看到Failure 的用法.
第三个版本的get_poetry:
def get_poetry(host, port, callback):
"""
Download a poem from the given host and port and invoke
callback(poem)
when the poem is complete. If there is a failure, invoke:
callback(err)
instead, where err is a twisted.python.failure.Failure instance.
"""
在这个版本后,我们在得到异常的时候还能同时得到一个traceback 记录.
我们已经快完成了,但是还有一个问题.处理错误和处理正常的结果看起来是一种很奇怪的行为.一般来说,我们会对出错和正常结果做出完全不同的操作.在同步的系统中我们会用try/except 语句来分别处理正确的和错误的结果:
try:
attempt_to_do_something_with_poetry()
except RhymeSchemeViolation:
# the code path when things go wrong
else:
# the code path when things go so, so right baby
如果我们也想保持这种错误处理的方式,我们需要让错误处理走另一条路径.在异步程序中分出一个路径意味着多出一个callback.
def get_poetry(host, port, callback, errback):
"""
Download a poem from the given host and port and invoke
callback(poem)
when the poem is complete. If there is a failure, invoke:
errback(err)
instead, where err is a twisted.python.failure.Failure instance.
"""
Client 3.1
client 3.1 在twisted-client-3/get-poetry-1.py 中,变化还是非常直观的, PoetryClientFactory 会获得一个callback 和一个errback,在clientConnectionFailed中调用了errback.
class PoetryClientFactory(ClientFactory):
protocol = PoetryProtocol
def __init__(self, callback, errback):
self.callback = callback
self.errback = errback
def poem_finished(self, poem):
self.callback(poem)
def clientConnectionFailed(self, connector, reason):
self.errback(reason)
既然clientConnectionFailed已经接收到一个包含错误信息的Failure 对象,我们把它传给errback 就可以了.其他的改变就很小了,我们就略去不讲了,你可以在不启动server 的情况下测试client 3.1:
python twisted-client-3/get-poetry-1.py 10004
你会看到下面的一些输出:
Poem failed: [Failure instance: Traceback (failure with no frames): : Connection was refused by other side: 111: Connection refused.
]
输出是从我们的poem_failed errback 输出的,在这种情况下,twisted 仅仅传递给我们一个Exception 而不是抛出,所以我们在这里不会得到一个traceback.但是一个traceback 不是必须的,因为这个地方不是一个bug.只是twisted 告诉我们,我们不能连接到那个地址.
Summary
下面是我们从第六部分学到的:
我们为twisted程序写的api必须是异步的
我们不能将同步的代码和异步的代码混合
在我们的代码中不能不用callback,就像twisted 那样
我们不得不用callback去处理错误
是不是意味着我们用twisted 写的每一个api都要包含callback和errback 两个参数?这样听起来可不是太美好.幸运的是twisted 已经用一个抽象把这两个参数消除掉了并带来一些新的特性.我们将在第七部分讲到.
twisted系列教程六–继续重构twisted poetry client相关推荐
- twisted系列教程十五–测试twisted代码
Introduction 在这个系列中我们也已经写了很多twisted 代码了,但目前为止我们忽略了一个很重要的事情-测试.你可能也一直在想我们怎样用一个同步的测试框架unitest来测试我们的异步的 ...
- 用python写诗歌网站要注意什么_Python Twisted系列教程5:由Twisted支持的诗歌客户端...
你可以从这里从头开始阅读这个系列 抽象地构建客户端 在第四部分中,我们构建了第一个使用Twisted的客户端.它确实能很好地工作,但仍有提高的空间. 首先是,这个客户端竟然有创建网络端口并接收端口处的 ...
- [转]Android Studio系列教程六--Gradle多渠道打包
转自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/ Android Studio系列教程六--Grad ...
- 以太坊构建DApps系列教程(六):使用定制代币进行投票
在本系列关于使用以太坊构建DApps教程的第5部分中,我们讨论了如何为Story添加内容,查看如何添加参与者从DAO购买代币的功能以及在Story中添加提交内容.现在是编写DAO最终形式的时候了:投票 ...
- 米思齐(Mixly)图形化系列教程(六)-for循环
目录 For执行过程 省略 省略'循环变量赋值' 省略'循环条件' 省略"循环变量增量" FOR循环使用举例 遍历数组 顺序输出数据 指定程序重复执行次数 死循环 求和 教程导航 ...
- PVE系列教程(六)、安装Windows11系统(专业版、企业版、家庭版通用)
为了更好的浏览体验,欢迎光顾勤奋的凯尔森同学个人博客 PVE系列教程(六).安装Windows11系统(专业版.企业版.家庭版通用) 一.创建win11的虚拟机,并设置参数 在PVE右上角点击创建虚拟 ...
- twisted系列教程五–改进twisted poetry client
在第四部分我们写了我们的第一个twisted client.它工作的非常好,但仍旧有提升的空间 首先,这个twisted client 包含了一些比如创建sockets 和从这些sockets 中接收 ...
- twisted系列教程十六–twisted守护进程
Introduction 到目前为止我们写的server 还运行在一个终端里面,通过print 语句向外输出内容.开发的时候这样做是很有好处的,但是当你部署一个产品的时候这样就不好了.一个生产环境中的 ...
- twisted系列教程九–Deferred 的第二个小插曲
More Consequence of Callbacks 我们将要再来研究一下callback,尽管我们已经对deferred比较了解而且已经可以写出twisted 风格的异步程序,Deferred ...
最新文章
- vue.js学习笔记(1)
- 【译】Java中的对象序列化
- 《Javascript高级程序设计》读书笔记之bind函数详解
- React中的状态管理---Mobx
- Java集合框架:WeakHashMap
- 前端学习(1320):同步和异步得区别
- Linux学习笔记020---CentOs7.3 搭建 Solr单机服务
- unity场景素材_[游戏素材]游戏场景环境天气变化Unity游戏素材资源
- javascript给类添加的方法
- JZOJ 1714. 小x的三角形(triangles.pas/cpp)
- 适合数码家电行业用的进销存,一个软件顶一百个人工不是梦
- matlab 黄金分析,优化算法之黄金分割算法-Matlab-站长-站长头条
- edi许可证和ICP区别
- 居于canvas的原生js抽奖小程序
- 谷歌浏览器被hao123绑定首页了
- 【死磕 Spring】----- IOC 之 Factory 实例化 bean
- 申请圣文森特牌照申请流程
- Ubuntu 16.04安装Matlab R2016b
- 抖音搬运视频热门技巧 剪辑后会修改视频md5
- netDxf实现对cad文件的读取与写入
热门文章
- PHP获取字符串的所有子集,PHP Regexp(PCRE)-查找所有子字符串的集合
- 电脑安装python后开不了机_Python学习第145课——关于无法正常启动centOS问题的解决方案...
- 中国电子学会scratch等级考试四级
- Java从零基础到精通教程全套视频课程
- 【七】Java面向对象
- Echarts笔记-折线图定制(Y轴百分数,鼠标移动显示百分数,显示X轴,Y轴值)
- Java文档阅读笔记-JPA中getOne()和findById的区别
- Qt文档阅读笔记-Qt插件DECLARE_INTERFACE METADATA INTERFACES官方解析及实例
- Qt模仿游戏拖装备(换装备)
- C++ STL list删除和修改