我们第一个twisted client
尽管twisted 经常被用来写server端的,但client往往会比较简单些,我们就以最简单的client 开始.源代码在twisted-client-1/get-poetry.py,首先开启server:

python blocking-server/slowpoetry.py --port 10000 poetry/ecstasy.txt
 --num-bytes 30
python blocking-server/slowpoetry.py --port 10001 poetry/fascination.txt
python blocking-server/slowpoetry.py --port 10002 poetry/science.txt

然后运行client:

python twisted-client-1/get-poetry.py 10000 10001 10002

你会看到如下的输出:

Task 1: got 60 bytes of poetry from 127.0.0.1:10000
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
Task 3: got 10 bytes of poetry from 127.0.0.1:10002
Task 1: got 30 bytes of poetry from 127.0.0.1:10000
Task 3: got 10 bytes of poetry from 127.0.0.1:10002
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
...
Task 1: 3003 bytes of poetry
Task 2: 623 bytes of poetry
Task 3: 653 bytes of poetry
Got 3 poems in 0:00:10.134220

就像我们先前写的异步的client一样,他们实质上做了相同的事情.让我们看一下源码看一下它是怎样工作的,把代码在你的编辑器中打开,以至于你知道我们在讨论什么.

就像在第一部分说的,我们刚开始接触twisted会尽量的用他的底层的api,这样我们更能了解twisted底层的东西.]
也就是说我们一开始用的一些底层api 在往后的项目中可能不会用到.我们只是学习练习twisted的用法.

这个twisted client 建立了几个PoetrySocket 对象.一个PoetrySocket 创造一个socket,连接一个server,然后设置非阻塞模式:

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(address)
self.sock.setblocking(0)

最后我们会将代码抽象到好像没有用到socket,不过现在还是要用到socket. 在我们创造一个连接之后,PoetrySocket 通过addReader 函数把它自己传递给reactor:

# tell the Twisted reactor to monitor this socket for reading
from twisted.internet import reactor
reactor.addReader(self)

这个函数交给twisted一个你想要监测的文件描述符. 为什么我传递给twisted 的是PoetySocket 对象而不是文件描述符和callback? twisted 是怎样知道怎样操作PoetySocket 的?相信我吧,我已经观察过了. 打开twisted.internet.interfaces模块,并跟随我.
Twisted Interfaces
在twisted 中有很多叫做接口的子模块.每一个模块定义了一些 接口类.在twisted 8.0 版本中, zope.interface是这些类的基础,但是这个包对我们不是特别重要,我们关心的是这些接口的子类,就像我们将要看到的这个.

作为一个python程序员你一定熟悉Duck Typing,一个对象的类型不是被它在类结构的中地位决定的,而是被它实现的公共的接口定义的(这里翻译的不太准确,建议看原文).因此两个实现了相同的公共接口的对象,他们是相同类型的.

你可以在twisted.internet.interfaces 中找到 addReader 的定义.它在 IReactorFDSet 接口中定义:

def addReader(reader):
    """
    I add reader to the set of file descriptors to get read events for.

@param reader: An L{IReadDescriptor} provider that will be checked for
                   read events until it is removed from the reactor with
                   L{removeReader}.

@return: C{None}.
    """

IReactorFDSet 是twisted reactors 实现的众多接口中的一个.因此任何的twisted reactor 对象都有一个addReader 的方法. 这个方法的声明并没有一个self 参数,因为它只是被定义为一个接口.接口对象永远不会被实例化或用作基类.

技术上讲,IReactorFDSet仅仅被reactor实现,被用来等待文件描述符,
    据我所知,IReactorFDSet 已经包含了reactor 的所有实现

zope.interface 允许你明确的声明一个类实现了一个或多个接口,并提供相应的机制在运行时来检查这些声明. 也支持适配器,可以动态的为一个对象提供一个它本身不支持的接口.感兴趣的同学移步这里
    你可能注意到了接口和抽象的基类的相同之处,都是对python 语言的扩展 ,我们在这里不讲他们之间的相同点和不同点.如果你感兴趣的话你可以读读 essay

根据上面讲的,addReader 方法的参数reader 应该实现IReadDescriptor接口.这就意味着我们的PoetrySocket 对象要实现这个接口. 我们可以找到IReadDescriptor 接口的定义:

class IReadDescriptor(IFileDescriptor):

def doRead():
        """
        Some data is available for reading on your descriptor.
        """

你会发现在我们的PoetrySocket 中doRead 方法的实现.当它被twised 的reactor调用时,它会从socket 中异步地读取数据.可见doRead 确实是一个callback,我们并没有把它直接传递给reactor,我们把它包装进PoetrySocket.这在twisted framework 中是个习惯–不是传递一个函数而是传递一个实现了某些接口的对象. 这样就允许我们一次可以传递多个callback,而且可以让多个callback可以通过一个对象中的共享数据互相通信.

PoetrySocket 对象的其他方法实现了什么?注意IReadDescriptor是IFileDescriptor子类,这就意味着实现了IReadDescriptor 的对象也必须实现IFileDescriptor,如果你看一下IFileDescriptor,你会发现:

class IFileDescriptor(ILoggingContext):
    """
    A file descriptor.
    """

def fileno():
        ...

def connectionLost(reason):
        ...

我省略了一部分注释,但是你可以从方法名中看到它们的用途:fileno 会返回我们想监听的文件描述符,connectionLost会在连接丢失的时候被访问.你会发现我们的PoetrySocket 也实现了这两个方法.

最后,IFileDescriptor继承至ILoggingContext,所以我们也要实现logPrefix callback,你可以在interfaces 模块中获取更详细的信息.

注意:你可能注意到doRead 会返回一个特殊值来表明什么时候这个socket被关闭.我是怎样知道这样做的呢?
一般来说,如果不这样做就不会工作,我查看了其他的实现了相同接口的方法.你不得不记住:有时候一些软件
的文档是不正确的.(略)

More on Callbacks
我们的新的twisetd client 和我们原先写的异步的client 确实很相像.两个client全都建立它们自己的socket,然后从这些socket 中异步的读数据.不同的是twsited cliet 不用自己实现select loop– 它用 twisted 的reactor 来实现.

doRead callback 是最重要的一个,twisted 通过它告诉我们socket 中已经有数据准备好了,我们可以把它形象化成图七

图片七

每一次callback被触发的时候,我们就尽可能的读数据然后停止(非阻塞的),就像我们在第三部分讲的,twisted 不能防止我们的代码是阻塞的.我们可以那样做一下然后看会发生什么. 在跟我们的twisted client 相同目录下有一个twisted-client-1/get-poetry-broken.py,这个client 跟咱们的twisted client 有两点不一样:

这个client 没有设置为非阻塞
    doRead方法不停的读数据直到一个socket被关闭

下面运行这个client:

python twisted-client-1/get-poetry-broken.py 10000 10001 10002

你将会得到以下的输出:

Task 1: got 3003 bytes of poetry from 127.0.0.1:10000
Task 3: got 653 bytes of poetry from 127.0.0.1:10002
Task 2: got 623 bytes of poetry from 127.0.0.1:10001
Task 1: 3003 bytes of poetry
Task 2: 623 bytes of poetry
Task 3: 653 bytes of poetry
Got 3 poems in 0:00:10.132753

输出跟我们以前的twisted client 不太一样,这是因为这个broken-client 为阻塞的.在callback中通过使用一个阻塞的操作,把我们以前的异步twisted程序编程了同步的twisted程序.我们得到了一个复杂的select 循环,并没有从异步中得到什么好处.

twisted 提供给我的多任务的处理方式是合作,twisted 会告诉什么时候去读什么时候去写,但是我们必须在尽可能处理多的数据情况下又要防止阻塞.此外我们必须避免其他的阻塞的操作,比如os.system.假如我们有一个很耗费cpu资源的任务,这就要求我们把它拆成小的交替的任务,以便i/o操作不会造成阻塞.

注意我们的broken- client仍旧会工作,但就是不能利用异步I/O 的效率,你可能仍旧注意到我们的broken-client 仍会比单纯的同步阻塞client 要快,那是因为broken -client 在一开始的时候就连接到所有的server,server端会立刻送出数据,操作系统会缓冲这些数据,即使我们没有读取它们,broken-client 会有效的获取到其他server传来的数据,即使broken-client在一段时间内只有一个在读.
这种buffer 的把戏只会在只有小量数据的时候管用,如果数据量很大的的话broken-client 会和我们以前的同步的client相差不多.

wrapping up

我对于我们的第一个twisted client 没有太多可说的,你应该注意到 connectionLostcallback在处理完所有的socket之后 关闭了这个reactor.这里并没有用到什么技巧,因为client 假设我们在下载完诗后不会再做其他的事情,我们还可以看到其他的两个底层的reactor api ,removeReader 和 getReaders.
在reactor api 中也还有Writer 相关的方法,被用来向文件描述符中写一些东西,你可以在interfaces 中获取更多信息.reader 和 writer 相关的api 会被twisted 分开写,因为select loop 会分开这两中事件.

在第五部分,我们将会用一些高级的api来写我们的twisted poetry.学习更多的接口和api. 谢谢 ~~~~

python twised系列教程四–twisted Poetry client相关推荐

  1. python Matplotlib 系列教程(四)——散点图

    这一章节,我们将介绍散点图的绘制: 散点图经常用来显示分布或者比较几个变量的相关性或者分组. 要绘制单个点我们需要使用scatter()函数,先看一个最简单的示例: # -*- coding: utf ...

  2. Python编程系列教程第12讲——属性和方法

    视频地址:http://v.youku.com/v_show/id_XNTgyOTg4NjQ4.html 普及网络安全知识,推动信息技术发展. 为祖国的网络安全撑起一片蓝天,为网络安全爱好者构建一方家 ...

  3. Python编程系列教程第16讲——拷贝自身到系统目录

    分享知识,分享快乐,收获友谊,收获财富! 大家好,我是数字雨,QQ:798033502 http://itbook.taobao.com/ 今天给大家带来的教程是<Python编程系列教程第16 ...

  4. python爬取图片教程-推荐|Python 爬虫系列教程一爬取批量百度图片

    Python 爬虫系列教程一爬取批量百度图片https://blog.csdn.net/qq_40774175/article/details/81273198# -*- coding: utf-8 ...

  5. 史上最详细的Android Studio系列教程四--Gradle基础

    史上最详细的Android Studio系列教程四--Gradle基础 转载于:https://www.cnblogs.com/zhujiabin/p/5125917.html

  6. Python编程系列教程第13讲——隐藏数据和封装

    视频地址:http://www.56.com/u88/v_OTM5NjU0MjE.html#fromoutpvid=OTM5NjU0MjE 普及网络安全知识,推动信息技术发展. 为祖国的网络安全撑起一 ...

  7. docker 打包镜像_Spring Boot2 系列教程(四十一)部署 Spring Boot 到远程 Docker 容器

    不知道各位小伙伴在生产环境都是怎么部署 Spring Boot 的,打成 jar 直接一键运行?打成 war 扔到 Tomcat 容器中运行?不过据松哥了解,容器化部署应该是目前的主流方案. 不同于传 ...

  8. python数据挖掘系列教程——PySpider框架应用全解

    全栈工程师开发手册 (作者:栾鹏) python教程全解 python数据挖掘系列教程--PySpider框架应用全解. PySpider介绍 pyspider上手更简单,操作更加简便,因为它增加了 ...

  9. python基础系列教程——数据结构(列表、元组、字典、集合、链表)

    全栈工程师开发手册 (作者:栾鹏) python数据挖掘系列教程 基本顺序存储结构--列表与元组 Python中的基本顺序存储结构是列表与元组,在操作的复杂度上和数组完全相同,其中列表是可变数据类型, ...

最新文章

  1. 手把手教你如何加入到github的开源世界! (转)
  2. 点云配准网络 PCRNet: Point Cloud Registration Network using PointNet Encoding 2019
  3. 清华出品:一文看尽AI芯片两类瓶颈三大趋势,存储技术开拓新疆界 | 附全文...
  4. Zookeeper是什么?
  5. LeetCode MySQL 1435. 制作会话柱状图
  6. Fresco 二三事:图片处理之旋转、缩放、裁剪切割图片
  7. vue项目中简单进行axios封装及响应状态码提示!
  8. 无服务器TOP3大关键问题及解决方案
  9. [NOI2014]动物园
  10. mysql 忘记root密码的解决办法
  11. ios vue 添加本地音乐_vue怎么添加自己的音乐
  12. 【网络课程设计】校园网规划方案
  13. codeblocks下载-安装和使用
  14. java log4j trace_关于LOG4J中的日志级别TRACE
  15. 计算机相关技术资料整理
  16. Niushop 商品分类、规格和类型之间的关系
  17. PG-FP6烧录机1拖16上位机项目
  18. 超好看的辅助网站源码
  19. 算法-入门篇(欧式距离)
  20. TP-LINK TL-WDN6200在Ubuntu 1804下使用

热门文章

  1. 最后一周|高级转录组分析和R语言数据可视化第十二期 (线上线下同时开课)...
  2. java win10 通知_如何在Java中创建Windows通知
  3. python gevent asyncio_python用from gevent import monkey; monkey.patch_all()之后报ssl等错误
  4. SQL工作笔记-达梦7存储过程中游标的使用(for循环 IF等)
  5. C/C++对象的序列化
  6. 让我们来比较C#,C++和Java之间重写虚函数的区别
  7. jasperreport转成html,JasperReport chart导出HTML不能显示chart图
  8. 前后端分离djangorestframework——序列化与反序列化数据
  9. linux awk菜鸟教程,Linux awk 命令
  10. mysql中函数大全_MySql 函数大全(一)