Client 4.0

既然我们已经对deferred有些了解了,我们可以用deferred 来重写我们的poetry client,你可以在这里找到client 4.0 twisted-client-4/get-poetry.py.

我们的get_poetry 函数不再需要callback 和 errback这两个参数.相反的,它返回一个deferred,我们可以在它上面附加一些callback 和errback.

def get_poetry(host, port):
    """
    Download a poem from the given host and port. This function
    returns a Deferred which will be fired with the complete text of
    the poem or a Failure if the poem could not be downloaded.
    """
    d = defer.Deferred()
    from twisted.internet import reactor
    factory = PoetryClientFactory(d)
    reactor.connectTCP(host, port, factory)
    return d

我们的factory 对象初始化的时候不再传入callback/errback 对,而是传入一个deferred 对象.一但我们得到诗或者我们发现我们不能连接上server,deferred 就会被触发–带着一首诗 或者错误.

class PoetryClientFactory(ClientFactory):

protocol = PoetryProtocol

def __init__(self, deferred):
        self.deferred = deferred

def poem_finished(self, poem):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.callback(poem)

def clientConnectionFailed(self, connector, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)

注意在deferred被触发之后我们释放deferred 的方法.这是一个在twisted源代码中使用的模式,可以帮助我们确保我们不会触发相同的deferred两次.也可以让python 的垃圾回收机更容易的回收资源.

在一次的,我们也不需要修改PoetryProtocol,还有一个需要修改的地方是poetry_main 函数:

def poetry_main():
    addresses = parse_args()

from twisted.internet import reactor

poems = []
    errors = []

def got_poem(poem):
        poems.append(poem)

def poem_failed(err):
        print >>sys.stderr, 'Poem failed:', err
        errors.append(err)

def poem_done(_):
        if len(poems) + len(errors) == len(addresses):
            reactor.stop()

for address in addresses:
        host, port = address
        d = get_poetry(host, port)
        d.addCallbacks(got_poem, poem_failed)
        d.addBoth(poem_done)

reactor.run()

for poem in poems:
        print poem

注意我们是怎样利用deferred 的callback 链来重构poem_done. 因为deferred在twisted中应用的这么频繁,所以我们经常用一个字符d 来持有你正在用的deferred对象,如果要做长期的存储对象,比如作为一个对象的属性,就经常被叫做”deferred”.

Discussion

我们的新的client 的get_poetry 和我们之前写的同步的版本的get_poetry 接收相同的参数–poetry server 的地址.同步的版本返回一首诗,而我们的异步版本返回一个deferred.返回deferred对象是twisted 所有特有的,这就指出了deferred的另一种概念:

一个Deferred对象描述了一个"异步的结果"或者说"还没有到来的结果"

我们可以用下面的图片描述这两种编程的模式:


图片十三

通过返回一个deferred,异步的api可以返回用户如下的信息:

我是一个异步的方法.你想让我做的事情我现在还没有做,但是当我做完的时候,我会触发这个deferred的callback链,并传递返回结果.

当然,这个方法本身不会迭代的触发deferred,它已经返回了.而是这个方法已经在返回的结果上动态的装置了一系列的事件,然后最终导致deferred被触发.

所以deferred 实际上是一种时间移位的方法,能让一个函数的返回结果来适应异步模型的需要.一个函数返回一个deferred 意味着这个函数是异步的,一种将来再返回结果的表现,一个结果会延迟的承诺.

一个同步的函数返回deferred也是可能的,技术上讲,deferred 返回一个值意味着这个函数可能是异步的,我们将会看到同步的函数返回deferred 的例子

因为deferred 的行为是很明确和被很多人知道的,如果你写的apis也返回deferred 你的程序会很容易的被理解和被复用的.如果没有deferred,每一个twisted 的程序或者每一个twisted 的组件可能都会有它们自己的唯一的用来处理callback 的方法,这样会增加你的学习成本.

When You’re Using Deferreds, You’re Still Using Callbacks, and They’re Still Invoked by the Reactor

当你一开始学习twisted 的时候,一个经常范的错误就是向deferred中添加很多的callback,特别的,人们经常认为向一个函数中加入足够多的callback它就是异步的了.这会导致你认为你可以在callback中用os.system,它就不是非阻塞的了.

我认为这个错误是因为你还没有真正的理解异步模型.因为典型的twisted 代码用了很多的deferred 而且很少会用到reactor,他导致你认为deferred做了全部的工作.如果你是从一开始就读的这个系列,你就会明白远不是这种情况.尽管twisted 是由很多工作在一起的部分组成的,但实现异步模型的任务是reactor 来完成的.deferred 是个很重要的抽象,但是我们不用它也可以写我们的twisted client.

让我们看一下我们第一个callback被触发时候的堆栈信息.运行twisted-client-4/get-poetry-stack.py(记得运行server),你会看到下面的输出:

File "twisted-client-4/get-poetry-stack.py", line 129, in
poetry_main()
File "twisted-client-4/get-poetry-stack.py", line 122, in poetry_main
reactor.run()

... # some more Twisted function calls

protocol.connectionLost(reason)
File "twisted-client-4/get-poetry-stack.py", line 59, in connectionLost
self.poemReceived(self.poem)
File "twisted-client-4/get-poetry-stack.py", line 62, in poemReceived
self.factory.poem_finished(poem)
File "twisted-client-4/get-poetry-stack.py", line 75, in poem_finished
d.callback(poem) # here's where we fire the deferred

... # some more methods on Deferreds

File "twisted-client-4/get-poetry-stack.py", line 105, in got_poem
traceback.print_stack()

和client 2.0 版本的堆栈信息非常相像,我们可以用图片十四来形象的描述:

图片十四

和我们前一个版本的client非常相像,(看到这张图片你想到了什么,哈哈,作者表达的既幽默又隐讳:for the sake of the children).这张图片没有表达出的一点是:callback 链不会把控制权返回给reactor,直到第二个callback被触发,也就是在第一个callback返回结果之后.

client4.0 和client2.0 中输出的堆栈信息中有一点不同的是,"twisted code " 和"our code" 的界限变得模糊了,自从deferred 的方法之后是真正的twisted code.这种"twisted code" 和 "our code" 的交互在大型的twisted 项目中是非常常见的.

通过在twisted中使用deferred我们已经在callback 链中多增加了几步,但是我们改变异步模型的基本原理.回想一下callback 程序的一些事实:

在任一时间只有一个callback在运行

当reactor 在运行的时候,我们的程序暂停
    和上一条相反,当我们的程序在运行的时候,reactor暂停
    如果callback阻塞了,则整个程序都会阻塞

向deferred上多增加一个callback不会改变这些事实.特别地,一个阻塞的callback仍旧会阻塞即使它被加进一个deferred中.所以那个deferred被触发的时候也会阻塞,则整个程序都是阻塞的.我们得出下面的结论: deferred 是一个管理callbacks 的方法,它们不是一个可以避免阻塞 和 可以把阻塞变成非阻塞的方法

我们可以通过构建一个带有阻塞callback 的deferred来证明最后一点.看一下这个例子:twisted-deferred/defer-block.py.第二个callback用time.sleep 进行阻塞.如果你运行那个程序然后检查print语句的顺序,它非常明确的说明了一个阻塞的callback也会在deferred中阻塞.

Summary

通过返回一个deferred,一个函数告诉用户我是异步的,并提供了一个机制去获得异步的结果.deferred 在twisted 中应用非常广泛,如果你查看twisted 的api,你会发现它.所以熟悉deferred 会给你带来回报的.Client 4.0 是第一个用twisted 范写出来的,使用deferred作为一个异步函数的返回值.还有一些twisted api 可以让我们把它变得更干净些,但是我想它已经描述了怎么用twisted写程序. 最后我们还会用twisted来写我们的server.

但是我们跟deferred还没完.deferred 类 用很短的一段代码提供了很多有特色的api.我们将会讲更多的deferred 的特色,请关注第九部分.

twisted系列教程八–延迟的诗相关推荐

  1. PVE系列教程(八)、安装OpenWRT软路由

    PVE系列教程(八).安装OpenWRT软路由 为了更好的浏览体验,欢迎光顾勤奋的凯尔森同学个人博客http://www.huerpu.cc:7000/ 本教程固件下载地址:http://nas.he ...

  2. SpringBoot 系列教程(八十五):Spring Boot使用MD5加盐验签Api接口之前后端分离架构设计

    加密算法参考: 浅谈常见的七种加密算法及实现 加密算法参考: 加密算法(DES,AES,RSA,MD5,SHA1,Base64)比较和项目应用 目的: 通过对API接口请求报文签名,后端进行验签处理, ...

  3. ClickHouse系列教程八:从一个服务器导入4T数据到另外一个服务器

    ClickHouse系列教程: ClickHouse系列教程 遇到了一个问题,就是如何把数据从一个服务器导入到另外一个服务器. 最初的想法是把数据都导出到CSV文件,然后再从CSV文件导入,做法如下: ...

  4. ASP.NET Core Web Razor Pages系列教程八: 添加验证

    系列文章目录:系列教程:使用ASP.NET Core创建Razor Pages Web应用程序 - zhangpeterx的博客 系列教程代码的GitHub地址:ASP .Net Core Razor ...

  5. Redis系列教程(八):分布式锁的由来、及Redis分布式锁的实现详解

    在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢?今天来探讨分布式锁这个话题. ...

  6. twisted系列教程七–小插曲,延迟对象

    在第六部分,我们得出这样一个结论:callbacks 是twisted异步编程的一个重要组成部分.callback 是交织在twisted结构中的,而不仅仅是连接reactor 的一种方法.所以用tw ...

  7. twisted系列教程十八–异步操作的并行运行

    Introduction 在上一部分我们学习了一种新的用生成器来组织一系列异步callbacks 的方法.加上deferred,我们已经有两种组织异步操作的方法了. 有时候,我们想让一组异步操作并行的 ...

  8. twisted系列教程十–可以变化的诗

    Client 5.0 现在我们将要想我们的client中加入一些变形逻辑.但是首先我不得不说:我不知道怎样写一个Byronification 引擎,它超出我的能力范围了.做为替代,我会实现一个相对简单 ...

  9. twisted系列教程十九–cancel deferred

    Introduction twisted 是一个正在发展的项目,twisted 的开发者们会添加一些新的特色或者扩展旧的.随着twisted 10.1.0 的发布,开发者们增加了一个新的功能–取消,这 ...

最新文章

  1. c语言队列如何表示,队列的链式表示和实现(C语言)
  2. git reset用法
  3. C++ Primer 5th笔记(chap 17 标准库特殊设施)未格式化的输入/输出操作
  4. 饥荒海难创建显示专用服务器,饥荒创建世界时一直显示启动服务器 | 手游网游页游攻略大全...
  5. 乔布斯,影响了一个时代的人
  6. java se开发_JAVA_SE基础——3.Java程序的开发流程
  7. linux函数嵌套,gcc内嵌函数__builtin_types_compatible_p 在内核中的一个实例...
  8. MySQL懒查询_mysql 联查的基本命令
  9. mysql事务最好别用_理解完这些基本上能解决面试中MySql的事务问题
  10. Python笔记:变量的作用域
  11. 关于Scrapy爬虫项目运行和调试的小技巧(上篇)
  12. 基于Java开发的五子棋游戏APP设计与实现
  13. PTA实验4-1-3 找出最小值 (20分) 本题要求编写程序,找出给定一系列整数中的最小值。
  14. Python的excel工作簿写入与读取操作
  15. 祭 事 本
  16. Pandas-数据结构-DataFrame(七):添加元素、修改元素、删除元素
  17. 程序员的选择,技术or管理
  18. 电商浪潮过后,无人零售会引领新零售的爆发吗
  19. 视觉检测设计与实践答题卡检测实验报告
  20. 想了解更多的话就请加QQ或者访问我的新浪博客

热门文章

  1. 用Python一次性把论文作图与数据处理全部搞定!
  2. 「超级右键」Mac必备的一款软件,新手get!
  3. centos 需要哪些常用端口_Docker 最常用的镜像命令和容器命令
  4. P2386 放苹果 方法一
  5. android 应用专属目录,获取Android应用专属缓存存储目录的实例
  6. IDEA集成Scala图文教程详细步骤
  7. 计算机报名忻州考点,2020山西省考忻州考区考点安排
  8. php截取字符串几位数,php按照指定长度截取字符串的代码
  9. mysql如何实现逻辑自增_每日一面 - mysql 的自增 id 的实现逻辑是什么样子的?
  10. python色标_Python: 气象绘图实例之台风