Twisted 是一个 Python 的事件驱动网络引擎。它诞生于 2000 年代初期,当时网络游戏开发者在任何语言中都无法获得可扩展的跨平台库。Twisted 的作者尝试在现有的网络环境中开发游戏,遇到了困难,他们看到了对可扩展、事件驱动、跨平台网络框架的明显需求,并决定将其实现,从过去游戏和网络应用程序开发人员的错误和困难中吸取教训。
Twisted 支持许多常见的传输层和应用层协议,包括 TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC 和 FTP。像它的编写语言一样,它是“电池包含的”;Twisted 附带了所有协议的客户端和服务器实现,以及使从命令行轻松配置和部署生产级 Twisted 应用程序的实用程序。
2000 年,Twisted 的创建者 glyph 正在开发一个名为 Twisted Reality 的基于文本的多人游戏。这是一个由线程组成的巨大混乱,每个连接有 3 个线程。一个线程用于阻塞读取的输入,一个线程用于阻塞某种写入的输出,还有一个“逻辑”线程在等待计时器到期或事件排队时休眠。当玩家在虚拟景观中移动并交互时,线程会发生死锁,缓存会损坏,锁定逻辑也永远无法完全正确——线程的使用使软件变得复杂、有漏洞且难以扩展。
他寻求替代方案,发现了 Python,特别是 Python 用于从套接字和管道等流对象中复用 I/O 的 select
模块(单一 UNIX 规范,版本 3 (SUSv3) 描述了 select
API)。当时,Java 并没有公开操作系统的 select
接口或任何其他异步 I/O API(用于非阻塞 I/O 的 java.nio
包是在 2002 年发布的 J2SE 1.4 中添加的)。使用 select
的 Python 游戏的快速原型立即证明比线程版本更简单且更可靠。
glyph 立即成为 Python、select
和事件驱动编程的拥护者,他使用 select
API 用 Python 为游戏编写了客户端和服务器。但后来他想做更多的事情。从根本上说,他想能够将网络活动转换为游戏中的对象上的方法调用。如果可以在游戏中接收电子邮件,就像 Nethack 邮件守护程序一样,那该怎么办?如果游戏中每个玩家都有一个主页,那该怎么办?glyph 发现自己需要使用 select
的良好的 Python IMAP 和 HTTP 客户端和服务器。
他首先转向 Medusa,这是一个在 90 年代中期开发的平台,用于在基于 asyncore
模块的 Python 中编写网络服务器。asyncore
是一个异步套接字处理程序,它在操作系统的 select
API 之上构建了一个调度程序和回调接口。
这对 glyph 来说是一个鼓舞人心的发现,但 Medusa 有两个缺点
asyncore
是围绕套接字的非常薄的包装器,应用程序程序员仍然需要直接操作套接字。这意味着可移植性仍然是程序员的责任。此外,当时,asyncore
的 Windows 支持存在漏洞,glyph 知道他想要在 Windows 上运行一个 GUI 客户端。glyph 面临着自己实现网络平台的前景,并意识到 Twisted Reality 打开了一扇通往一个与他的游戏一样有趣问题的门。
随着时间的推移,Twisted Reality 游戏变成了 Twisted 网络平台,它将做 Python 中现有网络平台没有做的事情
Twisted 是一个事件驱动网络引擎。事件驱动编程对于 Twisted 的设计理念如此重要,以至于值得花点时间回顾一下事件驱动编程的含义。
事件驱动编程是一种编程范式,其中程序流程由外部事件决定。它的特点是事件循环和使用回调在事件发生时触发动作。另外两种常见的编程范式是(单线程)同步编程和多线程编程。
让我们用一个例子比较和对比单线程、多线程和事件驱动编程模型。图 21.1 显示了程序在三种模型下随着时间的推移所完成的工作。程序有三个任务要完成,每个任务在等待 I/O 完成时都会阻塞。在 I/O 上阻塞的时间被灰显。
在程序的单线程同步版本中,任务是串行执行的。如果一个任务在 I/O 上阻塞一段时间,那么所有其他任务都必须等到它完成,然后按顺序执行。这种明确的顺序和串行处理很容易理解,但如果任务彼此之间不依赖,却仍然必须互相等待,那么程序就会变得不必要地慢。
在程序的线程版本中,三个在工作时阻塞的任务在单独的控制线程中执行。这些线程由操作系统管理,可以在多个处理器上同时运行,或者在一个处理器上交错运行。这使得某些线程可以在其他线程阻塞在资源上时继续进行。这通常比类似的同步程序更有效率,但人们必须编写代码来保护可能从多个线程并发访问的共享资源。多线程程序可能更难理解,因为现在人们必须担心通过进程序列化(锁定)、可重入性、线程本地存储或其他机制来保证线程安全,如果实现不当,可能会导致细微而痛苦的错误。
程序的事件驱动版本交错执行三个任务,但在单个控制线程中。执行 I/O 或其他昂贵操作时,会向事件循环注册一个回调,然后在 I/O 完成时继续执行。回调描述了如何在事件完成后处理该事件。事件循环轮询事件,并在事件到达时将它们分派给正在等待它们的回调。这使得程序能够在可以进行时取得进展,而无需使用额外的线程。事件驱动程序可能比多线程程序更容易理解,因为程序员不必担心线程安全。
事件驱动模型通常是以下情况下的一个好选择
当应用程序必须在任务之间共享可变数据时,这也是一个不错的选择,因为不需要执行同步。
网络应用程序通常正好具有这些特性,这使得它们非常适合事件驱动编程模型。
当 Twisted 创建时,许多针对各种网络协议的流行客户端和服务器已经存在。为什么 glyph 没有仅仅使用 Apache、IRCd、BIND、OpenSSH 或任何其他预先存在的应用程序,这些应用程序的客户端和服务器必须为 Twisted 从头开始重新实现?
问题在于所有这些服务器实现都包含从头开始编写的网络代码,通常用 C 编写,并将应用程序代码直接耦合到网络层。这使得它们非常难以用作库。在使用它们时,必须将它们视为黑盒子,开发者没有机会在想要通过多个协议公开相同数据时重用代码。此外,服务器和客户端实现通常是独立的应用程序,不共享代码。扩展这些应用程序并维护跨平台的客户端-服务器兼容性比它需要做的更难。
使用 Twisted,客户端和服务器是用 Python 编写,使用一致的接口。这使得编写新的客户端和服务器变得容易,在客户端和服务器之间共享代码,在协议之间共享应用程序逻辑以及测试代码变得容易。
Twisted 实现反应堆设计模式,该模式描述了在单线程环境中将来自多个来源的事件解复用并分派给它们的处理程序。
Twisted 的核心是反应堆事件循环。反应堆了解网络、文件系统和计时器事件。它等待并处理这些事件,抽象掉平台特定的行为,并提供接口来使在网络堆栈的任何地方响应事件变得容易。
反应堆本质上完成了
while True: timeout = time_until_next_timed_event() events = wait_for_events(timeout) events += timed_events_until(now()) for event in events: event.process()
基于 poll
API 的反应堆(在单一 UNIX 规范,版本 3 (SUSv3) 中描述)是目前所有平台上的默认反应堆。Twisted 还支持许多平台特定的高容量复用 API。平台特定的反应堆包括基于 FreeBSD 的 kqueue
机制的 KQueue 反应堆,一个基于 epoll
接口的 epoll
反应堆(目前是 Linux 2.6),以及一个基于 Windows 输入/输出完成端口的 IOCP 反应堆。
Twisted 处理的轮询实现依赖于平台的细节示例包括
Twisted 的反应堆实现还负责正确使用底层的非阻塞 API 并正确处理模糊的边缘情况。Python 根本没有公开 IOCP API,因此 Twisted 保持了自己的实现。
回调是事件驱动编程的根本部分,是反应堆向应用程序指示事件已完成的方式。随着事件驱动程序的增长,处理应用程序中事件的成功和错误情况变得越来越复杂。如果没有注册适当的回调,程序可能会阻塞在永远不会发生的事件处理上,错误可能不得不从网络堆栈向上传播到应用程序的各个层中的回调链。
让我们通过比较 Python 类伪代码中玩具 URL 获取实用程序的同步和异步版本来检查事件驱动程序的一些陷阱
同步 URL 获取器
import getPage def processPage(page): print page def logError(error): print error def finishProcessing(value): print "Shutting down..." exit(0) url = "http://google.com" try: page = getPage(url) processPage(page) except Error, e: logError(error) finally: finishProcessing()
异步 URL 获取器
from twisted.internet import reactor import getPage def processPage(page): print page finishProcessing() def logError(error): print error finishProcessing() def finishProcessing(value): print "Shutting down..." reactor.stop() url = "http://google.com" # getPage takes: url, # success callback, error callback getPage(url, processPage, logError) reactor.run()
在异步 URL 获取器中,reactor.run()
启动反应堆事件循环。在同步和异步版本中,假设的 getPage
函数执行页面检索的工作。如果检索成功,则调用 processPage
,如果在尝试检索页面时引发了 Exception
,则调用 logError
。无论哪种情况,都会在之后调用 finishProcessing
。
异步版本中 logError
的回调反映了同步版本中 try/except
块的 except
部分。processPage
的回调反映了 else
,finishProcessing
的无条件回调反映了 finally
。
在同步版本中,由于try/except
块的结构,logError
和processPage
中恰好只调用一个,并且finishProcessing
始终调用一次;在异步版本中,程序员有责任调用正确的成功和错误回调链。如果由于编程错误,在各自的回调链中省略了对processPage
或logError
的finishProcessing
调用,反应堆将永远不会停止,程序将永远运行。
这个玩具示例暗示了 Twisted 开发的头几年让程序员感到沮丧的复杂性。Twisted 通过扩展一个名为Deferred
的对象来应对这种复杂性。
Deferred
对象是对尚未存在的结果的概念的抽象。它还有助于管理此结果的回调链。当由函数返回时,Deferred
是对该函数将在某个时间点有结果的承诺。该单个返回的Deferred
包含对为某个事件注册的所有回调的引用,因此只需要将此对象在函数之间传递,这比单独管理回调更容易跟踪。
Deferred
有两个回调链,一个用于成功(回调),一个用于错误(errbacks)。Deferred
从两个空链开始。一个添加回调和 errbacks 对以处理事件处理中每个点的成功和失败。当异步结果到达时,“触发”Deferred
,并按添加的顺序调用相应的回调或 errbacks。
以下是使用Deferred
的异步 URL 获取器伪代码版本
from twisted.internet import reactor import getPage def processPage(page): print page def logError(error): print error def finishProcessing(value): print "Shutting down..." reactor.stop() url = "http://google.com" deferred = getPage(url) # getPage returns a Deferred deferred.addCallbacks(success, logError) deferred.addBoth(stop) reactor.run()
在这个版本中,调用了相同的事件处理程序,但它们都与单个Deferred
对象注册,而不是分散在代码中并作为参数传递给getPage
。
Deferred
创建了两个阶段的回调。首先,addCallbacks
将processPage
回调和logError
errback添加到各自链的第一个阶段。然后addBoth
将finishProcessing
添加到两个链的第二个阶段。从图表上看,回调链看起来像图 21.2。
Deferred
只能触发一次;尝试重新触发它们将引发Exception
。这使Deferred
的语义更接近于其同步同类的try/except
块的语义,这使得异步事件处理更容易推理,并避免了由回调对单个事件调用一次以上或以下次数而造成的微妙错误。
理解Deferred
是理解 Twisted 程序流程的重要部分。但是,在使用 Twisted 为网络协议提供的更高级别抽象时,通常根本不需要直接使用Deferred
。
Deferred
抽象功能强大,已被许多其他事件驱动的平台借用,包括 jQuery、Dojo 和 Mochikit。
传输表示通过网络通信的两个端点之间的连接。传输负责描述连接详细信息,例如面向流或数据报、流量控制和可靠性。TCP、UDP 和 Unix 套接字是传输的示例。它们旨在成为“最小功能单元,最大程度地可重用”,并且与协议实现分离,允许许多协议使用相同类型的传输。传输实现ITransport
接口,该接口具有以下方法
write | 以非阻塞方式按顺序将一些数据写入物理连接。 |
writeSequence | 将字符串列表写入物理连接。 |
loseConnection | 写入所有挂起的 data,然后关闭连接。 |
getPeer | 获取此连接的远程地址。 |
getHost | 获取此连接端点的地址。 |
将传输与协议分离也使测试这两个层变得更容易。模拟传输可以简单地将数据写入字符串以供检查。
协议描述如何异步处理网络事件。HTTP、DNS 和 IMAP 是应用程序协议的示例。协议实现IProtocol
接口,该接口具有以下方法
makeConnection | 建立与传输和服务器的连接。 |
connectionMade | 在建立连接时调用。 |
dataReceived | 在接收到数据时调用。 |
connectionLost | 在关闭连接时调用。 |
反应堆、协议和传输之间的关系最好用一个例子来说明。以下是回声服务器和客户端的完整实现,首先是服务器
from twisted.internet import protocol, reactor class Echo(protocol.Protocol): def dataReceived(self, data): # As soon as any data is received, write it back self.transport.write(data) class EchoFactory(protocol.Factory): def buildProtocol(self, addr): return Echo() reactor.listenTCP(8000, EchoFactory()) reactor.run()
以及客户端
from twisted.internet import reactor, protocol class EchoClient(protocol.Protocol): def connectionMade(self): self.transport.write("hello, world!") def dataReceived(self, data): print "Server said:", data self.transport.loseConnection() def connectionLost(self, reason): print "connection lost" class EchoFactory(protocol.ClientFactory): def buildProtocol(self, addr): return EchoClient() def clientConnectionFailed(self, connector, reason): print "Connection failed - goodbye!" reactor.stop() def clientConnectionLost(self, connector, reason): print "Connection lost - goodbye!" reactor.stop() reactor.connectTCP("localhost", 8000, EchoFactory()) reactor.run()
运行服务器脚本启动一个 TCP 服务器,监听端口 8000 上的连接。服务器使用Echo
协议,数据通过 TCP 传输写入。运行客户端将建立与服务器的 TCP 连接,回显服务器响应,然后终止连接并停止反应堆。工厂用于为连接的两端生成协议实例。通信在两端都是异步的;connectTCP
负责与反应堆注册回调,以便在有数据可从套接字读取时收到通知。
Twisted 是一个用于生成可扩展、跨平台网络服务器和客户端的引擎。在生产环境中以标准化方式轻松部署这些应用程序是类似平台获得广泛采用的重要部分。
为此,Twisted 开发了 Twisted 应用程序基础设施,这是一种可重用且可配置的部署 Twisted 应用程序的方法。它允许程序员通过将应用程序挂接到现有的工具来避免样板代码,从而自定义应用程序的运行方式,包括守护进程、日志记录、使用自定义反应堆、分析代码等等。
应用程序基础设施主要包含四个部分:服务、应用程序、配置管理(通过 TAC 文件和插件)以及twistd
命令行实用程序。为了说明这个基础设施,我们将上一节中的回声服务器变成一个应用程序。
服务是任何可以启动和停止并符合IService
接口的事物。Twisted 附带了对 TCP、FTP、HTTP、SSH、DNS 以及许多其他协议的服务实现。许多服务可以注册到单个应用程序。
IService
接口的核心是
startService | 启动服务。这可能包括加载配置数据、建立数据库连接或监听端口 |
stopService | 关闭服务。这可能包括将状态保存到磁盘、关闭数据库连接或停止监听端口 |
我们的回声服务使用 TCP,因此我们可以使用 Twisted 的默认TCPServer
实现这个IService
接口。
应用程序是表示整个 Twisted 应用程序的顶级服务。服务在应用程序中注册,下面描述的twistd
部署实用程序搜索并运行应用程序。
我们将创建一个回声应用程序,回声服务可以在其中注册。
在使用常规 Python 文件管理 Twisted 应用程序时,开发人员负责编写代码来启动和停止反应堆以及配置应用程序。在 Twisted 应用程序基础设施下,协议实现存在于模块中,使用这些协议的服务在 Twisted 应用程序配置 (TAC) 文件中注册,并且反应堆和配置由外部实用程序管理。
要将我们的回声服务器变成回声应用程序,我们可以遵循一个简单的算法
EchoFactory
的TCPServer
服务实例,并将其注册到应用程序中。管理反应堆的代码将由下面讨论的twistd
处理。应用程序代码最终看起来像这样
echo.py
文件
from twisted.internet import protocol, reactor class Echo(protocol.Protocol): def dataReceived(self, data): self.transport.write(data) class EchoFactory(protocol.Factory): def buildProtocol(self, addr): return Echo()
echo_server.tac
文件
from twisted.application import internet, service from echo import EchoFactory application = service.Application("echo") echoService = internet.TCPServer(8000, EchoFactory()) echoService.setServiceParent(application)
twistd
(发音为“twist-dee”)是一个用于部署 Twisted 应用程序的跨平台实用程序。它运行 TAC 文件并处理应用程序的启动和停止。作为 Twisted 一站式网络编程方法的一部分,twistd
附带了许多有用的配置标志,包括将应用程序守护进程化、日志文件的存放位置、降低权限、在 chroot 中运行、在非默认反应堆下运行,甚至在分析器下运行应用程序。
我们可以使用以下命令运行回声服务器应用程序
$ twistd -y echo_server.tac
在最简单的情况下,twistd
启动应用程序的守护进程实例,日志记录到twistd.log
。在启动和停止应用程序之后,日志看起来像这样
2011-11-19 22:23:07-0500 [-] Log opened. 2011-11-19 22:23:07-0500 [-] twistd 11.0.0 (/usr/bin/python 2.7.1) starting up. 2011-11-19 22:23:07-0500 [-] reactor class: twisted.internet.selectreactor.SelectReactor. 2011-11-19 22:23:07-0500 [-] echo.EchoFactory starting on 8000 2011-11-19 22:23:07-0500 [-] Starting factory <echo.EchoFactory instance at 0x12d8670> 2011-11-19 22:23:20-0500 [-] Received SIGTERM, shutting down. 2011-11-19 22:23:20-0500 [-] (TCP Port 8000 Closed) 2011-11-19 22:23:20-0500 [-] Stopping factory <echo.EchoFactory instance at 0x12d8670> 2011-11-19 22:23:20-0500 [-] Main loop terminated. 2011-11-19 22:23:20-0500 [-] Server Shut Down.
使用 Twisted 应用程序基础设施运行服务允许开发人员跳过为常见服务功能(如日志记录和守护进程化)编写样板代码。它还为部署应用程序建立了标准的命令行界面。
用于运行 Twisted 应用程序的 TAC 系统的替代方案是插件系统。虽然 TAC 系统使在应用程序配置文件中轻松注册预定义服务的简单层次结构成为可能,但插件系统使将自定义服务注册为twistd
实用程序的子命令以及扩展应用程序的命令行界面变得容易。
使用这个系统
要使用 Twisted 插件系统扩展程序,只需创建一个实现了IPlugin
接口的对象,并将它们放在插件系统知道在哪里查找它们的特定位置。
我们已经将回声服务器转换为 Twisted 应用程序,将其转换为 Twisted 插件非常简单。在之前包含Echo
协议和EchoFactory
定义的echo
模块旁边,我们添加了一个名为twisted
的目录,其中包含一个名为plugins
的子目录,其中包含我们的回声插件定义。这个插件将允许我们启动一个回声服务器,并将要使用的端口指定为twistd
实用程序的参数
from zope.interface import implements from twisted.python import usage from twisted.plugin import IPlugin from twisted.application.service import IServiceMaker from twisted.application import internet from echo import EchoFactory class Options(usage.Options): optParameters = [["port", "p", 8000, "The port number to listen on."]] class EchoServiceMaker(object): implements(IServiceMaker, IPlugin) tapname = "echo" description = "A TCP-based echo server." options = Options def makeService(self, options): """ Construct a TCPServer from a factory defined in myproject. """ return internet.TCPServer(int(options["port"]), EchoFactory()) serviceMaker = EchoServiceMaker()
现在,我们的回声服务器将在twistd --help
的输出中显示为服务器选项,运行twistd echo --port=1235
将在端口 1235 上启动回声服务器。
Twisted 带有一个名为twisted.cred
的可插拔服务器身份验证系统,插件系统的一个常见用途是向应用程序添加身份验证模式。可以使用twisted.cred AuthOptionMixin
添加对各种身份验证类型的命令行支持,或者添加新的类型。例如,可以使用插件系统通过本地 Unix 密码数据库或 LDAP 服务器添加身份验证。
twistd
附带了对 Twisted 支持的许多协议的插件,这将启动服务器的工作变成一个单一的命令。以下是 Twisted 附带的一些twistd
服务器示例
twistd web --port 8080 --path .
twistd dns -p 5553 --hosts-file=hosts
hosts
的文件中的域名,该文件的格式与 /etc/hosts
相同。sudo twistd conch -p tcp:2222
twistd mail -E -H localhost -d localhost=emails
emails
目录。twistd
使得轻松启动服务器以测试客户端变得容易,但它也是可插拔的生产级代码。
在这方面,Twisted 通过 TAC 文件、插件和 twistd
实现的应用程序部署机制取得了成功。然而,根据轶事,大多数大型 Twisted 部署最终不得不重写其中一些管理和监控设施;体系结构并没有完全公开系统管理员需要的功能。这反映了 Twisted 在历史上并没有从系统管理员那里获得太多架构方面的意见——而系统管理员是部署和维护应用程序的专家。
Twisted 在未来在这个领域的架构决策中,更积极地征求专家级最终用户的反馈将大有裨益。
Twisted 最近庆祝了其 10 周年纪念。自成立以来,受 2000 年代初网络游戏领域的启发,它在很大程度上实现了其成为可扩展、跨平台、事件驱动的网络引擎的目标。Twisted 被 Google 和 Lucasfilm 以及 Justin.TV 和 Launchpad 软件协作平台等公司的生产环境使用。Twisted 中的服务器实现是许多其他开源应用程序的核心,包括 BuildBot、BitTorrent 和 Tahoe-LAFS。
自 Twisted 首次开发以来,它的架构变化很少。上面讨论过的 Deferred
是一个重要的补充,用于管理挂起的结果及其回调链。
有一个重要的删除,它在当前的实现中几乎没有占用空间:Twisted 应用程序持久性。
Twisted 应用程序持久性 (TAP) 是一种将应用程序的配置和状态保存在 pickle 中的方法。使用这种方案运行应用程序是一个两步过程
mktap
实用程序创建表示应用程序的 pickle。twistd
解开 pickle 并运行应用程序。这个过程的灵感来自于 Smalltalk 映像,对看似随意的配置语言的激增(这些语言难以编写脚本)的厌恶,以及希望用 Python 表达配置细节的愿望。
TAP 文件立即引入了不需要的复杂性。Twisted 中的类会发生变化,而不会改变 pickle 中这些类的实例。尝试在 pickle 对象上使用 Twisted 的更新版本的类方法或属性会导致应用程序崩溃。引入了用于将 pickle 升级到新 API 版本的“升级器”的概念,但是,为了涵盖所有可能的升级路径,必须维护升级器、pickle 版本和单元测试的矩阵,而全面考虑所有接口更改仍然是困难且容易出错的。
TAP 及其相关实用程序被放弃,然后最终从 Twisted 中删除,并被 TAC 文件和插件取代。TAP 的首字母缩写变成了 Twisted Application Plugin,Twisted 中几乎没有关于这个失败的 pickling 系统的痕迹。
从 TAP fiasco 中吸取的教训是,为了保持合理的可维护性,持久数据需要一个显式的模式。更一般地说,这是一个关于向项目添加复杂性的教训:当考虑引入一个新系统来解决问题时,请确保该解决方案的复杂性得到充分理解和测试,并且在将项目提交给它之前,确保这些好处明显值得增加的复杂性。
虽然不是主要架构决策,但关于重写 Twisted Web 实现的项目管理决策对 Twisted 的形象以及维护者对代码库其他部分进行架构改进的能力产生了长期影响,因此值得简短讨论一下。
在 2000 年代中期,Twisted 开发人员决定在 Twisted 代码库中以名为 web2
的单独项目形式对 twisted.web
API 进行完整重写。web2
将包含比 twisted.web
更多的改进,包括完整的 HTTP 1.1 支持和流式数据 API。
web2
被标记为实验性的,但最终还是被主要项目使用,甚至意外地被 Debian 发布和打包。web
和 web2
的开发同时进行了几年,新用户一直对这两个项目的并存以及缺乏关于使用哪个项目的明确信息感到沮丧。向 web2
的切换从未发生,最终在 2011 年 web2
从代码库和网站中删除。web2
中的一些改进正在慢慢地移植回 web
。
部分由于 web2
,Twisted 形成了一种难以导航且结构混乱的声誉,对于新手来说更是如此。多年后,Twisted 社区仍在努力消除这种形象。
从 web2
中吸取的教训是,从头开始重写项目通常是一个糟糕的主意,但如果必须这样做,请确保开发人员社区了解长期计划,并且用户社区在重写期间只有一个明确的实现选择可以使用。
如果 Twisted 可以重做 web2
,开发人员会对 twisted.web
进行一系列向后兼容的更改和弃用,而不是重写。
我们使用互联网的方式在不断发展。将许多协议实现为核心软件的一部分的决定,给 Twisted 带来了维护所有这些协议代码的负担。实现必须随着不断变化的标准和新协议的采用而发展,同时还要保持严格的向后兼容性策略。
Twisted 主要是一个由志愿者驱动的项目,开发的限制因素不是社区的热情,而是志愿者的时间。例如,定义 HTTP 1.1 的 RFC 2616 于 1999 年发布,在 2005 年开始为 Twisted 的 HTTP 协议实现添加 HTTP 1.1 支持,这项工作于 2009 年完成。对 IPv6 的支持,定义于 1998 年的 RFC 2460 中,截至 2011 年仍在进行,但尚未合并。
实现还必须随着受支持的操作系统暴露的接口的变化而发展。例如,epoll
事件通知设施于 2002 年添加到 Linux 2.5.44 中,Twisted 扩展了一个基于 epoll
的反应堆来利用这个新的 API。2007 年,苹果发布了 OS 10.5 Leopard,其中包含一个不支持设备的 poll
实现,这个实现的错误行为足以让苹果在其构建的 Python 中不暴露 select.poll
。从那时起,Twisted 就一直在解决这个问题并为用户记录这个问题。
有时,Twisted 开发无法跟上不断变化的网络环境,增强功能被转移到核心软件之外的库中。例如,Wokkel 项目是 Twisted 的 Jabber/XMPP 支持的增强功能集合,多年来一直作为一个待合并的独立项目存在,没有一个冠军来监督合并。在浏览器开始在 2009 年采用对新协议的支持之后,有人尝试将 WebSockets 添加到 Twisted 中,但在决定在该协议从 IETF 草案变为标准之前不包含该协议之后,开发工作转移到了外部项目。
尽管如此,库和附加组件的激增证明了 Twisted 的灵活性和可扩展性。严格的测试驱动开发策略以及伴随的文档和编码标准帮助该项目避免了回归,并保留了向后兼容性,同时维护了受支持的协议和平台的大量矩阵。这是一个成熟、稳定的项目,它仍然拥有非常活跃的开发和采用。
Twisted 希望在未来十年继续成为您互联网的引擎。