Rumah pembangunan bahagian belakang Tutorial Python 剖析Python的Twisted框架的核心特性

剖析Python的Twisted框架的核心特性

Feb 03, 2017 pm 04:17 PM

一. reactor
twisted的核心是reactor,而提到reactor不可避免的是同步/异步,阻塞/非阻塞,在Dave的第一章概念性介绍中,对同步/异步的界限有点模糊,关于同步/异步,阻塞/非阻塞可参见知乎讨论。而关于proactor(主动器)和reactor(反应堆),这里有一篇推荐博客有比较详细的介绍。
就reactor模式的网络IO而言,应该是同步IO而不是异步IO。而Dave第一章中提到的异步,核心在于:显式地放弃对任务的控制权而不是被操作系统随机地停止,程序员必须将任务组织成序列来交替的小步完成。因此,若其中一个任务用到另外一个任务的输出,则依赖的任务(即接收输出的任务)需要被设计成为要接收系列比特或分片而不是一下全部接收。
显式主动地放弃任务的控制权有点类似协程的思考方式,reactor可看作协程的调度器。reactor是一个事件循环,我们可以向reactor注册自己感兴趣的事件(如套接字可读/可写)和处理器(如执行读写操作),reactor会在事件发生时回调我们的处理器,处理器执行完成之后,相当于协程挂起(yield),回到reactor的事件循环中,等待下一个事件来临并回调。reactor本身有一个同步事件多路分解器(Synchronous Event Demultiplexer),可用select/epoll等机制实现,当然twisted reactor的事件触发不一定是基于IO,也可以由定时器等其它机制触发。
twisted的reactor无需我们主动注册事件和回调函数,而是通过多态(继承特定类,并实现所关心的事件接口,然后传给twisted reactor)来实现。关于twisted的reactor,有几个需要注意的地方:
twisted.internet.reactor是单例模式,每个程序只能有一个reactor;
尽量在reactor回调函数尽快完成操作,不要执行阻塞任务,reactor本质是单线程,用户回调代码与twisted代码运行在同一个上下文,某个回调函数中阻塞,会导致reactor整个事件循环阻塞;
reactor会一直运行,除非通过reactor.stop()显示停止它,但一般调用reactor.stop(),也就意味着应用程序结束;

二. twisted简单使用
twisted的本质是reactor,我们可以使用twisted的底层API(避开twisted便利的高层抽象)来使用reactor:

# 示例一 twisted底层API的使用
from twisted.internet import reacto
from twisted.internet import main
from twisted.internet.interfaces import IReadDescriptor
import socket
 
class MySocket(IReadDescriptor):
  def __init__(self, address):
    # 连接服务器
    self.address = address
    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.sock.connect(address)
    self.sock.setblocking(0)
 
    # tell the Twisted reactor to monitor this socket for reading
    reactor.addReader(self)
  
 # 接口: 告诉reactor 监听的套接字描述符
  def fileno(self):
    try:
      return self.sock.fileno()
    except socket.error:
      return -1
       
 # 接口: 在连接断开时的回调
  def connectionLost(self, reason):
    self.sock.close()
 
    reactor.removeReader(self)
  
 # 当应用程序需要终止时 调用:
    # reactor.stop()
 
 # 接口: 当套接字描述符有数据可读时
  def doRead(self):
    bytes = ''
 
 # 尽可能多的读取数据
    while True:
      try:
        bytesread = self.sock.recv(1024)
        if not bytesread:
          break
        else:
          bytes += bytesread
      except socket.error, e:
        if e.args[0] == errno.EWOULDBLOCK:
          break
        return main.CONNECTION_LOST
 
    if not bytes: 
      return main.CONNECTION_DONE
    else:
      # 在这里解析协议并处理数据
      print bytes
Salin selepas log masuk

示例一可以很清晰的看到twisted的reactor本质:添加监听描述符,监听可读/可写事件,当事件来临时回调函数,回调完成之后继续监听事件。
需要注意:
套接字为非阻塞,如果为阻塞则失去了reactor的意义
我们通过继承IReadDescriptor来提供reactor所需要的接口
通过reactor.addReader将套接字类加入reactor的监听对象中
main.CONNECTION_LOST是twisted预定义的值,通过这些值它我们可以一定程度控制下一步回调(类似于模拟一个事件)
但是上面的MySocket类不够好,主要有以下缺点:
需要我们自己去读取数据,而不是框架帮我们读好,并处理异常
网络IO和数据处理混为一块,没有剥离开来

三. twisted抽象
twisted在reactor的基础上,建立了更高的抽象,对一个网络连接而言,twisted建立了如下三个概念:
Transports:网络连接层,仅负责网络连接和读/写字节数据
Protocols: 协议层,服务业务相关的网络协议,将字节流转换成应用所需数据
Protocol Factories:协议工厂,负责创建Protocols,每个网络连接都有一个Protocols对象(因为要保存协议解析状态)
twisted的这些概念和erlang中的ranch网络框架很像,ranch框架也抽象了Transports和Protocols概念,在有新的网络连接时,ranch自动创建Transports和Protocols,其中Protocols由用户在启动ranch时传入,是一个实现了ranch_protocol behaviour的模块,Protocols初始化时,会收到该连接对应的Transports,如此我们可以在Protocols中处理字节流数据,按照我们的协议解析并处理数据。同时可通过Transports来发送数据(ranch已经帮你读取了字节流数据了)。
和ranch类似,twisted也会在新连接到达时创建Protocols并且将Transport传入,twisted会帮我们读取字节流数据,我们只需在dataReceived(self, data)接口中处理字节流数据即可。此时的twisted在网络IO上可以算是真正的异步了,它帮我们处理了网络IO和可能遇到的异常,并且将网络IO和数据处理剥离开来,抽象为Transports和Protocols,提高了程序的清晰性和健壮性。

# 示例二 twisted抽象的使用
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ClientFactory
class MyProtocol(Protocol):
  
 # 接口: Protocols初始化时调用,并传入Transports
 # 另外 twisted会自动将Protocols的factory对象成员设为ProtocolsFactory实例的引用
 #   如此就可以通过factory来与MyProtocolFactory交互
 def makeConnection(self,trans):
    print 'make connection: get transport: ', trans
    print 'my factory is: ', self.factory
     
 # 接口: 有数据到达
  def dataReceived(self, data):
    self.poem += data
    msg = 'Task %d: got %d bytes of poetry from %s'
    print msg % (self.task_num, len(data), self.transport.getPeer())
  
 # 接口: 连接断开
  def connectionLost(self, reason):
    # 连接断开的处理
 
 
class MyProtocolFactory(ClientFactory):
 
 # 接口: 通过protocol类成员指出需要创建的Protocols
  protocol = PoetryProtocol # tell base class what proto to build
 
  def __init__(self, address):
    self.poetry_count = poetry_count
    self.poems = {} # task num -> poem
     
 # 接口: 在创建Protocols的回调
  def buildProtocol(self, address):
    proto = ClientFactory.buildProtocol(self, address)
    # 在这里对proto做一些初始化....
    return proto
     
 # 接口: 连接Server失败时的回调
  def clientConnectionFailed(self, connector, reason):
    print 'Failed to connect to:', connector.getDestination()
     
def main(address):
 factory = MyClientFactory(address)
  host, port = address
  # 连接服务端时传入ProtocolsFactory
  reactor.connectTCP(host, port, factory) 
  reactor.run()
Salin selepas log masuk

示例二要比示例一要简单清晰很多,因为它无需处理网络IO,并且逻辑上更为清晰,实际上ClientFactory和Protocol提供了更多的接口用于实现更灵活强大的逻辑控制,具体的接口可参见twisted源代码。

四. twisted Deferred
twisted Deferred对象用于解决这样的问题:有时候我们需要在ProtocolsFactory中嵌入自己的回调,以便Protocols中发生某个事件(如所有Protocols都处理完成)时,回调我们指定的函数(如TaskFinished)。如果我们自己来实现回调,需要处理几个问题:
如何区分回调的正确返回和错误返回?(我们在使用异步调用时,要尤其注意错误返回的重要性)
如果我们的正确返回和错误返回都需要执行一个公共函数(如关闭连接)呢?
如果保证该回调只被调用一次?
Deferred对象便用于解决这种问题,它提供两个回调链,分别对应于正确返回和错误返回,在正确返回或错误返回时,它会依次调用对应链中的函数,并且保证回调的唯一性。

d = Deferred()
# 添加正确回调和错误回调
d.addCallbacks(your_ok_callback, your_err_callback)
# 添加公共回调函数
d.addBoth(your_common_callback)
 
# 正确返回 将依次调用 your_ok_callback(Res) -> common_callback(Res)
d.callback(Res)
# 错误返回 将依次调用 your_err_callback(Err) -> common_callback(Err)
d.errback(Err)
 
# 注意,对同一个Defered对象,只能返回一次,尝试多次返回将会报错
Salin selepas log masuk

twisted的defer是异步的一种变现方式,可以这么理解,他和thread的区别是,他是基于时间event的。
有了deferred,即可对任务的执行进行管理控制。防止程序的运行,由于等待某项任务的完成而陷入阻塞停滞,提高整体运行的效率。
Deferred能帮助你编写异步代码,但并不是为自动生成异步或无阻塞的代码!要想将一个同步函数编程异步函数,必须在函数中返回Deferred并正确注册回调。

五.综合示例

下面的例子,你们自己跑跑,我上面说的都是一些个零散的例子,大家对照下面完整的,走一遍。 twisted理解其实却是有点麻烦,大家只要知道他是基于事件的后,慢慢理解就行了。

#coding:utf-8
#xiaorui.cc
from twisted.internet import reactor, defer
from twisted.internet.threads import deferToThread
import os,sys
from twisted.python import threadable; threadable.init(1)
deferred =deferToThread.__get__
import time
def todoprint_(result):
  print result
def running():
  "Prints a few dots on stdout while the reactor is running."
#   sys.stdout.write("."); sys.stdout.flush()
  print '.'
  reactor.callLater(.1, running)
@deferred
def sleep(sec):
  "A blocking function magically converted in a non-blocking one."
  print 'start sleep %s'%sec
  time.sleep(sec)
  print '\nend sleep %s'%sec
  return "ok"
def test(n,m):
  print "fun test() is start"
  m=m
  vals = []
  keys = []
  for i in xrange(m):
    vals.append(i)
    keys.append('a%s'%i)
  d = None
  for i in xrange(n):
    d = dict(zip(keys, vals))
  print "fun test() is end"
  return d
if __name__== "__main__":
#one
  sleep(10).addBoth(todoprint_)
  reactor.callLater(.1, running)
  reactor.callLater(3, reactor.stop)
  print "go go !!!"
  reactor.run()
#two
  aa=time.time()
  de = defer.Deferred()
  de.addCallback(test)
  reactor.callInThread(de.callback,10000000,100 )
  print time.time()-aa
  print "我这里先做别的事情"
  print de
  print "go go end"
Salin selepas log masuk

更多剖析Python的Twisted框架的核心特性相关文章请关注PHP中文网!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Repo: Cara menghidupkan semula rakan sepasukan
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Cara mendapatkan biji gergasi
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Cara Menggunakan Python untuk Mencari Pengagihan Zipf Fail Teks Cara Menggunakan Python untuk Mencari Pengagihan Zipf Fail Teks Mar 05, 2025 am 09:58 AM

Tutorial ini menunjukkan cara menggunakan Python untuk memproses konsep statistik undang -undang ZIPF dan menunjukkan kecekapan membaca dan menyusun fail teks besar Python semasa memproses undang -undang. Anda mungkin tertanya -tanya apa maksud pengedaran ZIPF istilah. Untuk memahami istilah ini, kita perlu menentukan undang -undang Zipf. Jangan risau, saya akan cuba memudahkan arahan. Undang -undang Zipf Undang -undang Zipf hanya bermaksud: Dalam korpus bahasa semulajadi yang besar, kata -kata yang paling kerap berlaku muncul kira -kira dua kali lebih kerap sebagai kata -kata kerap kedua, tiga kali sebagai kata -kata kerap ketiga, empat kali sebagai kata -kata kerap keempat, dan sebagainya. Mari kita lihat contoh. Jika anda melihat corpus coklat dalam bahasa Inggeris Amerika, anda akan melihat bahawa perkataan yang paling kerap adalah "th

Bagaimana saya menggunakan sup yang indah untuk menghuraikan html? Bagaimana saya menggunakan sup yang indah untuk menghuraikan html? Mar 10, 2025 pm 06:54 PM

Artikel ini menerangkan cara menggunakan sup yang indah, perpustakaan python, untuk menghuraikan html. Ia memperincikan kaedah biasa seperti mencari (), find_all (), pilih (), dan get_text () untuk pengekstrakan data, pengendalian struktur dan kesilapan HTML yang pelbagai, dan alternatif (sel

Penapisan gambar di python Penapisan gambar di python Mar 03, 2025 am 09:44 AM

Berurusan dengan imej yang bising adalah masalah biasa, terutamanya dengan telefon bimbit atau foto kamera resolusi rendah. Tutorial ini meneroka teknik penapisan imej di Python menggunakan OpenCV untuk menangani isu ini. Penapisan Imej: Alat yang berkuasa Penapis Imej

Pengenalan kepada pengaturcaraan selari dan serentak di Python Pengenalan kepada pengaturcaraan selari dan serentak di Python Mar 03, 2025 am 10:32 AM

Python, kegemaran sains dan pemprosesan data, menawarkan ekosistem yang kaya untuk pengkomputeran berprestasi tinggi. Walau bagaimanapun, pengaturcaraan selari dalam Python memberikan cabaran yang unik. Tutorial ini meneroka cabaran -cabaran ini, memberi tumpuan kepada Interprete Global

Bagaimana untuk melakukan pembelajaran mendalam dengan Tensorflow atau Pytorch? Bagaimana untuk melakukan pembelajaran mendalam dengan Tensorflow atau Pytorch? Mar 10, 2025 pm 06:52 PM

Artikel ini membandingkan tensorflow dan pytorch untuk pembelajaran mendalam. Ia memperincikan langkah -langkah yang terlibat: penyediaan data, bangunan model, latihan, penilaian, dan penempatan. Perbezaan utama antara rangka kerja, terutamanya mengenai grap pengiraan

Cara Melaksanakan Struktur Data Anda Sendiri di Python Cara Melaksanakan Struktur Data Anda Sendiri di Python Mar 03, 2025 am 09:28 AM

Tutorial ini menunjukkan mewujudkan struktur data saluran paip tersuai di Python 3, memanfaatkan kelas dan pengendali yang berlebihan untuk fungsi yang dipertingkatkan. Fleksibiliti saluran paip terletak pada keupayaannya untuk menggunakan siri fungsi ke set data, GE

Serialization dan deserialisasi objek python: Bahagian 1 Serialization dan deserialisasi objek python: Bahagian 1 Mar 08, 2025 am 09:39 AM

Serialization dan deserialization objek Python adalah aspek utama dari mana-mana program bukan remeh. Jika anda menyimpan sesuatu ke fail python, anda melakukan siri objek dan deserialization jika anda membaca fail konfigurasi, atau jika anda menjawab permintaan HTTP. Dalam erti kata, siri dan deserialization adalah perkara yang paling membosankan di dunia. Siapa yang peduli dengan semua format dan protokol ini? Anda mahu berterusan atau mengalirkan beberapa objek python dan mengambilnya sepenuhnya pada masa yang akan datang. Ini adalah cara yang baik untuk melihat dunia pada tahap konseptual. Walau bagaimanapun, pada tahap praktikal, skim siri, format atau protokol yang anda pilih boleh menentukan kelajuan, keselamatan, kebebasan status penyelenggaraan, dan aspek lain dari program

Modul Matematik dalam Python: Statistik Modul Matematik dalam Python: Statistik Mar 09, 2025 am 11:40 AM

Modul Statistik Python menyediakan keupayaan analisis statistik data yang kuat untuk membantu kami dengan cepat memahami ciri -ciri keseluruhan data, seperti biostatistik dan analisis perniagaan. Daripada melihat titik data satu demi satu, cuma melihat statistik seperti min atau varians untuk menemui trend dan ciri dalam data asal yang mungkin diabaikan, dan membandingkan dataset besar dengan lebih mudah dan berkesan. Tutorial ini akan menjelaskan cara mengira min dan mengukur tahap penyebaran dataset. Kecuali dinyatakan sebaliknya, semua fungsi dalam modul ini menyokong pengiraan fungsi min () dan bukan hanya menjumlahkan purata. Nombor titik terapung juga boleh digunakan. Import secara rawak Statistik import dari fracti

See all articles