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相关推荐

  1. twisted系列教程十五–测试twisted代码

    Introduction 在这个系列中我们也已经写了很多twisted 代码了,但目前为止我们忽略了一个很重要的事情-测试.你可能也一直在想我们怎样用一个同步的测试框架unitest来测试我们的异步的 ...

  2. 用python写诗歌网站要注意什么_Python Twisted系列教程5:由Twisted支持的诗歌客户端...

    你可以从这里从头开始阅读这个系列 抽象地构建客户端 在第四部分中,我们构建了第一个使用Twisted的客户端.它确实能很好地工作,但仍有提高的空间. 首先是,这个客户端竟然有创建网络端口并接收端口处的 ...

  3. [转]Android Studio系列教程六--Gradle多渠道打包

    转自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/ Android Studio系列教程六--Grad ...

  4. 以太坊构建DApps系列教程(六):使用定制代币进行投票

    在本系列关于使用以太坊构建DApps教程的第5部分中,我们讨论了如何为Story添加内容,查看如何添加参与者从DAO购买代币的功能以及在Story中添加提交内容.现在是编写DAO最终形式的时候了:投票 ...

  5. 米思齐(Mixly)图形化系列教程(六)-for循环

    目录 For执行过程 省略 省略'循环变量赋值' 省略'循环条件' 省略"循环变量增量" FOR循环使用举例 遍历数组 顺序输出数据 指定程序重复执行次数 死循环 求和 教程导航 ...

  6. PVE系列教程(六)、安装Windows11系统(专业版、企业版、家庭版通用)

    为了更好的浏览体验,欢迎光顾勤奋的凯尔森同学个人博客 PVE系列教程(六).安装Windows11系统(专业版.企业版.家庭版通用) 一.创建win11的虚拟机,并设置参数 在PVE右上角点击创建虚拟 ...

  7. twisted系列教程五–改进twisted poetry client

    在第四部分我们写了我们的第一个twisted client.它工作的非常好,但仍旧有提升的空间 首先,这个twisted client 包含了一些比如创建sockets 和从这些sockets 中接收 ...

  8. twisted系列教程十六–twisted守护进程

    Introduction 到目前为止我们写的server 还运行在一个终端里面,通过print 语句向外输出内容.开发的时候这样做是很有好处的,但是当你部署一个产品的时候这样就不好了.一个生产环境中的 ...

  9. twisted系列教程九–Deferred 的第二个小插曲

    More Consequence of Callbacks 我们将要再来研究一下callback,尽管我们已经对deferred比较了解而且已经可以写出twisted 风格的异步程序,Deferred ...

最新文章

  1. vue.js学习笔记(1)
  2. 【译】Java中的对象序列化
  3. 《Javascript高级程序设计》读书笔记之bind函数详解
  4. React中的状态管理---Mobx
  5. Java集合框架:WeakHashMap
  6. 前端学习(1320):同步和异步得区别
  7. Linux学习笔记020---CentOs7.3 搭建 Solr单机服务
  8. unity场景素材_[游戏素材]游戏场景环境天气变化Unity游戏素材资源
  9. javascript给类添加的方法
  10. JZOJ 1714. 小x的三角形(triangles.pas/cpp)
  11. 适合数码家电行业用的进销存,一个软件顶一百个人工不是梦
  12. matlab 黄金分析,优化算法之黄金分割算法-Matlab-站长-站长头条
  13. edi许可证和ICP区别
  14. 居于canvas的原生js抽奖小程序
  15. 谷歌浏览器被hao123绑定首页了
  16. 【死磕 Spring】----- IOC 之 Factory 实例化 bean
  17. 申请圣文森特牌照申请流程
  18. Ubuntu 16.04安装Matlab R2016b
  19. 抖音搬运视频热门技巧 剪辑后会修改视频md5
  20. netDxf实现对cad文件的读取与写入

热门文章

  1. PHP获取字符串的所有子集,PHP Regexp(PCRE)-查找所有子字符串的集合
  2. 电脑安装python后开不了机_Python学习第145课——关于无法正常启动centOS问题的解决方案...
  3. 中国电子学会scratch等级考试四级
  4. Java从零基础到精通教程全套视频课程
  5. 【七】Java面向对象
  6. Echarts笔记-折线图定制(Y轴百分数,鼠标移动显示百分数,显示X轴,Y轴值)
  7. Java文档阅读笔记-JPA中getOne()和findById的区别
  8. Qt文档阅读笔记-Qt插件DECLARE_INTERFACE METADATA INTERFACES官方解析及实例
  9. Qt模仿游戏拖装备(换装备)
  10. C++ STL list删除和修改