卷2:第21章 Twisted

2018-02-24 15:55 更新

原文鏈接:http://www.aosabook.org/en/twisted.html

作者:Jessica McKellar

Twisted是用Python實(shí)現(xiàn)的基于事件驅(qū)動(dòng)的網(wǎng)絡(luò)引擎框架。Twisted誕生于2000年初,在當(dāng)時(shí)的網(wǎng)絡(luò)游戲開發(fā)者看來,無論他們使用哪種語言,手中都鮮有可兼顧擴(kuò)展性及跨平臺(tái)的網(wǎng)絡(luò)庫(kù)。Twisted的作者試圖在當(dāng)時(shí)現(xiàn)有的環(huán)境下開發(fā)游戲,這一步走的非常艱難,他們迫切地需要一個(gè)可擴(kuò)展性高、基于事件驅(qū)動(dòng)、跨平臺(tái)的網(wǎng)絡(luò)開發(fā)框架,為此他們決定自己實(shí)現(xiàn)一個(gè),并從那些之前的游戲和網(wǎng)絡(luò)應(yīng)用程序的開發(fā)者中學(xué)習(xí),汲取他們的經(jīng)驗(yàn)教訓(xùn)。

Twisted支持許多常見的傳輸及應(yīng)用層協(xié)議,包括TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC以及FTP。就像Python一樣,Twisted也具有“內(nèi)置電池”(batteries-included)的特點(diǎn)。Twisted對(duì)于其支持的所有協(xié)議都帶有客戶端和服務(wù)器實(shí)現(xiàn),同時(shí)附帶有基于命令行的工具,使得配置和部署產(chǎn)品級(jí)的Twisted應(yīng)用變得非常方便。

21.1 為什么需要Twisted

2000年時(shí),Twisted的作者Glyph正在開發(fā)一個(gè)名為Twisted Reality的基于文本方式的多人在線游戲。這個(gè)游戲采用Java開發(fā),里面盡是一堆線程——每個(gè)連接就有3個(gè)線程處理。處理輸入的線程會(huì)在讀操作上阻塞,處理輸出的線程將在一些寫操作上阻塞,還有一個(gè)“邏輯”線程將在等待定時(shí)器超時(shí)或者事件入隊(duì)列時(shí)休眠。隨著玩家們?cè)谔摂M世界中移動(dòng)并交互時(shí),線程出現(xiàn)死鎖,緩存被污染,程序中的加鎖邏輯幾乎從來就沒對(duì)過——采用多線程使得整個(gè)軟件變得復(fù)雜、漏洞百出而且極難擴(kuò)展。

為了尋求其他的解決方案,作者發(fā)現(xiàn)了Python,特別是Python中用于對(duì)流式對(duì)象比如socket和pipe進(jìn)行多路I/O復(fù)用的select模塊(UNIX規(guī)范第3版(SUSv3)描述了select)。那時(shí),Java并沒有提供操作系統(tǒng)的select接口或者任何其他的異步I/O API(針對(duì)非阻塞式I/O的包java.nio已經(jīng)在J2SE 1.4中加入了,2002年發(fā)布)。通過用Python中的select模塊快速搭建起游戲的原型,這迅速降低了程序的復(fù)雜度,并且比多線程版本要更加可靠。

Glyph迅速轉(zhuǎn)向了Python、select以及基于事件驅(qū)動(dòng)的編程。他使用Python的select模塊為游戲編寫了客戶端和服務(wù)器。但他想要的還不止于此。從根本上說,他希望能將網(wǎng)絡(luò)行為轉(zhuǎn)變?yōu)閷?duì)游戲中的對(duì)象的方法調(diào)用。如果你能在游戲中收取郵件會(huì)怎樣,就像Nethack mailer這種守護(hù)進(jìn)程一樣?如果游戲中的每位玩家都擁有一個(gè)主頁呢?Glyph發(fā)現(xiàn)他需要優(yōu)秀的IMAP以及HTTP客戶端和服務(wù)器的Python實(shí)現(xiàn),而這些都要采用select。

他首先轉(zhuǎn)向了Medusa,這是一個(gè)在90年代中期開發(fā)的平臺(tái),在這里可以采用Python中的asyncore模塊來編寫網(wǎng)絡(luò)服務(wù)。asyncore是一個(gè)異步化處理socket的模塊,在操作系統(tǒng)的select API之上構(gòu)建了一個(gè)調(diào)度器和回調(diào)接口。

這對(duì)于Glyph來說是個(gè)激動(dòng)人心的發(fā)現(xiàn),但Medusa有兩個(gè)缺點(diǎn):

  1. 這個(gè)項(xiàng)目到2001年就不再維護(hù)了,那正是glyph開發(fā)Twisted Reality的時(shí)候。
  2. asyncore只是對(duì)socket的一個(gè)薄封裝層,應(yīng)用程序的編寫者仍然需要直接操作socket。這意味著程序可移植性的擔(dān)子仍然落在程序員自己身上。此外,那時(shí)asyncore對(duì)Windows的支持還有問題,Glyph希望能在Windows上運(yùn)行一個(gè)帶有圖形用戶界面的客戶端。

Glyph需要自己實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)引擎平臺(tái),而且他意識(shí)到Twisted Reality已經(jīng)打開了問題的大門,這和他的游戲一樣有趣。

隨著時(shí)間的推移,Twisted Reality這個(gè)游戲就演化成了Twisted網(wǎng)絡(luò)引擎平臺(tái)。它可以做到當(dāng)時(shí)Python中已有的網(wǎng)絡(luò)平臺(tái)所無法做到的事情:

  • 使用基于事件驅(qū)動(dòng)的編程模型,而不是多線程模型。
  • 跨平臺(tái):為主流操作系統(tǒng)平臺(tái)暴露出的事件通知系統(tǒng)提供統(tǒng)一的接口。
  • “內(nèi)置電池”的能力:提供流行的應(yīng)用層協(xié)議實(shí)現(xiàn),因此Twisted馬上就可為開發(fā)人員所用。
  • 符合RFC規(guī)范,已經(jīng)通過健壯的測(cè)試套件證明了其一致性。
  • 能很容易的配合多個(gè)網(wǎng)絡(luò)協(xié)議一起使用。
  • 可擴(kuò)展。

21.2 Twisted架構(gòu)概覽

Twisted是一個(gè)事件驅(qū)動(dòng)型的網(wǎng)絡(luò)引擎。由于事件驅(qū)動(dòng)編程模型在Twisted的設(shè)計(jì)哲學(xué)中占有重要的地位,因此這里有必要花點(diǎn)時(shí)間來回顧一下究竟事件驅(qū)動(dòng)意味著什么。

事件驅(qū)動(dòng)編程是一種編程范式,這里程序的執(zhí)行流由外部事件來決定。它的特點(diǎn)是包含一個(gè)事件循環(huán),當(dāng)外部事件發(fā)生時(shí)使用回調(diào)機(jī)制來觸發(fā)相應(yīng)的處理。另外兩種常見的編程范式是(單線程)同步以及多線程編程。

讓我們用例子來比較和對(duì)比一下單線程、多線程以及事件驅(qū)動(dòng)編程模型。圖21.1展示了隨著時(shí)間的推移,這三種模式下程序所做的工作。這個(gè)程序有3個(gè)任務(wù)需要完成,每個(gè)任務(wù)都在等待I/O操作時(shí)阻塞自身。阻塞在I/O操作上所花費(fèi)的時(shí)間已經(jīng)用灰色框標(biāo)示出來了。

運(yùn)行服務(wù)器端腳本將啟動(dòng)一個(gè)TCP服務(wù)器,監(jiān)聽端口8000上的連接。服務(wù)器采用的是Echo協(xié)議,數(shù)據(jù)經(jīng)TCP transport對(duì)象寫出。運(yùn)行客戶端腳本將對(duì)服務(wù)器發(fā)起一個(gè)TCP連接,回顯服務(wù)器端的回應(yīng)然后終止連接并停止reactor事件循環(huán)。這里的Factory用來對(duì)連接的雙方生成protocol對(duì)象實(shí)例。兩端的通信是異步的,connectTCP負(fù)責(zé)注冊(cè)回調(diào)函數(shù)到reactor事件循環(huán)中,當(dāng)socket上有數(shù)據(jù)可讀時(shí)通知回調(diào)處理。

Applications

Twisted是用來創(chuàng)建具有可擴(kuò)展性、跨平臺(tái)的網(wǎng)絡(luò)服務(wù)器和客戶端的引擎。在生產(chǎn)環(huán)境中,以標(biāo)準(zhǔn)化的方式簡(jiǎn)化部署這些應(yīng)用的過程對(duì)于Twisted這種被廣泛采用的平臺(tái)來說是非常重要的一環(huán)。為此,Twisted開發(fā)了一套應(yīng)用程序基礎(chǔ)組件,采用可重用、可配置的方式來部署Twisted應(yīng)用。這種方式使程序員避免堆砌千篇一律的代碼來將應(yīng)用程序同已有的工具整合在一起,這包括精靈化進(jìn)程(daemonization)、日志處理、使用自定義的reactor循環(huán)、對(duì)代碼做性能剖析等。

應(yīng)用程序基礎(chǔ)組件包含4個(gè)主要部分:服務(wù)(Service)、應(yīng)用(Application)、配置管理(通過TAC文件和插件)以及twistd命令行程序。為了說明這個(gè)基礎(chǔ)組件,我們將上一節(jié)的Echo服務(wù)器轉(zhuǎn)變成一個(gè)應(yīng)用。

Service

Service就是IService接口下實(shí)現(xiàn)的可以啟動(dòng)和停止的組件。Twisted自帶有TCP、FTP、HTTP、SSH、DNS等服務(wù)以及其他協(xié)議的實(shí)現(xiàn)。其中許多Service都可以注冊(cè)到單獨(dú)的應(yīng)用中。IService接口的核心是:

startService    啟動(dòng)服務(wù)??赡馨虞d配置數(shù)據(jù),設(shè)定數(shù)據(jù)庫(kù)連接或者監(jiān)聽某個(gè)端口
stopService     關(guān)閉服務(wù)。可能包含將狀態(tài)保存到磁盤,關(guān)閉數(shù)據(jù)庫(kù)連接或者停止監(jiān)聽端口

我們的Echo服務(wù)使用TCP協(xié)議,因此我們可以使用Twisted中IService接口下默認(rèn)的TCPServer實(shí)現(xiàn)。

Application

Application是處于最頂層的Service,代表了整個(gè)Twisted應(yīng)用程序。Service需要將其自身同Application注冊(cè),然后就可以用下面我們將介紹的部署工具twistd搜索并運(yùn)行應(yīng)用程序。我們將創(chuàng)建一個(gè)可以同Echo Service注冊(cè)的Echo應(yīng)用。

TAC文件

當(dāng)在一個(gè)普通的Python文件中管理Twisted應(yīng)用程序時(shí),需要由開發(fā)者負(fù)責(zé)編寫啟動(dòng)和停止reactor事件循環(huán)以及配置應(yīng)用程序的代碼。在Twisted的基礎(chǔ)組件中,協(xié)議的實(shí)現(xiàn)都是在一個(gè)模塊中完成的,需要使用到這些協(xié)議的Service可以注冊(cè)到一個(gè)Twisted應(yīng)用程序配置文件中(TAC文件)去,這樣reactor事件循環(huán)和程序配置就可以由外部組件來進(jìn)行管理。

要將我們的Echo服務(wù)器轉(zhuǎn)變成一個(gè)Echo應(yīng)用,我們可以按照以下幾個(gè)簡(jiǎn)單的步驟來完成:

  1. 將Echo服務(wù)器的Protocol部分移到它們自己所歸屬的模塊中去。

  2. 在TAC文件中:

    1. 創(chuàng)建一個(gè)Echo應(yīng)用。
    2. 創(chuàng)建一個(gè)TCPServer的Service實(shí)例,它將使用我們的EchoFactory,然后同前面創(chuàng)建的應(yīng)用完成注冊(cè)。

管理reactor事件循環(huán)的代碼將由twistd來負(fù)責(zé),我們下面會(huì)對(duì)此進(jìn)行討論。這樣,應(yīng)用程序的代碼就變成這樣了:

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()

twistd

twistd(讀作“twist-dee”)是一個(gè)跨平臺(tái)的用來部署Twisted應(yīng)用程序的工具。它執(zhí)行TAC文件并負(fù)責(zé)處理啟動(dòng)和停止應(yīng)用程序。作為Twisted在網(wǎng)絡(luò)編程中具有“內(nèi)置電池”能力的一部分,twistd自帶有一些非常有用的配置標(biāo)志,包括將應(yīng)用程序轉(zhuǎn)變?yōu)槭刈o(hù)進(jìn)程、定義日志文件的路徑、設(shè)定特權(quán)級(jí)別、在chroot下運(yùn)行、使用非默認(rèn)的reactor,甚至是在profiler下運(yùn)行應(yīng)用程序。

我們可以像這樣運(yùn)行這個(gè)Echo服務(wù)應(yīng)用:

$ twistd –y echo_server.tac

在這個(gè)簡(jiǎn)單的例子里,twistd將這個(gè)應(yīng)用程序作為守護(hù)進(jìn)程來啟動(dòng),日志記錄在twistd.log文件中。啟動(dòng)和停止應(yīng)用后,日志文件內(nèi)容如下:

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框架中的基礎(chǔ)組件來運(yùn)行服務(wù),這么做使得開發(fā)人員能夠不用再編寫類似守護(hù)進(jìn)程和記錄日志這樣的冗余代碼了。這同樣也為部署應(yīng)用程序建立了一個(gè)標(biāo)準(zhǔn)的命令行接口。

Plugins

對(duì)于運(yùn)行Twisted應(yīng)用程序的方法,除了基于TAC文件外還有一種可選的方法,這就是插件系統(tǒng)。TAC系統(tǒng)可以很方便的將Twisted預(yù)定義的服務(wù)同應(yīng)用程序配置文件注冊(cè),而插件系統(tǒng)能夠方便的將用戶自定義的服務(wù)注冊(cè)為twistd工具的子命令,然后擴(kuò)展應(yīng)用程序的命令行接口。

在使用插件系統(tǒng)時(shí):

  1. 由于只有plugin API需要保持穩(wěn)定,這使得第三方開發(fā)者能很容易地?cái)U(kuò)展軟件。

  2. 插件發(fā)現(xiàn)能力已經(jīng)集成到系統(tǒng)中了。插件可以在程序首次運(yùn)行時(shí)加載并保存,每次程序啟動(dòng)時(shí)會(huì)重新觸發(fā)插件發(fā)現(xiàn)過程,或者也可以在程序運(yùn)行期間反復(fù)輪詢新插件,這使得在程序已經(jīng)啟動(dòng)后我們還可以判斷是否有新的插件安裝上了。

當(dāng)使用Twisted插件系統(tǒng)來擴(kuò)展軟件時(shí),我們要做的就是創(chuàng)建IPlugin接口下實(shí)現(xiàn)的對(duì)象并將它們放到一個(gè)特定的位置中,這里插件系統(tǒng)知道該如何去找到它們。

我們已經(jīng)將Echo服務(wù)轉(zhuǎn)換為一個(gè)Twisted應(yīng)用程序了,而將其轉(zhuǎn)換為一個(gè)Twisted插件也是非常簡(jiǎn)單直接的。在我們之前的Echo模塊中,除了包含有Echo協(xié)議和EchoFactory的定義之外,現(xiàn)在我們還要添加一個(gè)名為twistd的目錄,其中還包含著一個(gè)名為plugins的子目錄,這里正是我們需要定義echo插件的地方。通過這個(gè)插件,我們可以啟動(dòng)一個(gè)echo服務(wù),并將需要使用的端口號(hào)作為參數(shù)指定給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()

現(xiàn)在,我們的Echo服務(wù)器將作為一個(gè)服務(wù)選項(xiàng)出現(xiàn)在twistd –help的輸出中。運(yùn)行twistd echo –port=1235將在端口1235上啟動(dòng)一個(gè)Echo服務(wù)器。

Twisted還帶有一個(gè)可拔插的針對(duì)服務(wù)器端認(rèn)證的模塊twisted.cred,插件系統(tǒng)常見的用途就是為應(yīng)用程序添加一個(gè)認(rèn)證模式。我們可以使用twisted.cred中現(xiàn)成的AuthOptionMixin類來添加針對(duì)各種認(rèn)證的命令行支持,或者是添加新的認(rèn)證類型。比如,我們可以使用插件系統(tǒng)來添加基于本地Unix密碼數(shù)據(jù)庫(kù)或者是基于LDAP服務(wù)器的認(rèn)證方式。

twistd工具中附帶有許多Twisted所支持的協(xié)議插件,只用一條單獨(dú)的命令就可以完成啟動(dòng)服務(wù)器的工作了。這里有一些通過twistd啟動(dòng)服務(wù)器的例子:

twistd web –port 8080 –path .

這條命令將在8080端口啟動(dòng)一個(gè)HTTP服務(wù)器,在當(dāng)前目錄中負(fù)責(zé)處理靜態(tài)和動(dòng)態(tài)頁面請(qǐng)求。

twistd dns –p 5553 –hosts-file=hosts

這條命令在端口5553上啟動(dòng)一個(gè)DNS服務(wù)器,解析指定的文件hosts中的域名,這個(gè)文件的內(nèi)容格式同/etc/hosts一樣。

sudo twistd conch –p tcp:2222

這條命令在端口2222上啟動(dòng)一個(gè)SSH服務(wù)器。ssh的密鑰必須獨(dú)立設(shè)定。

twistd mail –E –H localhost –d localhost=emails

這條命令啟動(dòng)一個(gè)ESMTP POP3服務(wù)器,為本地主機(jī)接收郵件并保存到指定的emails目錄下。

我們可以方便的通過twistd來搭建一個(gè)用于測(cè)試客戶端功能的服務(wù)器,但它同樣是可裝載的、產(chǎn)品級(jí)的服務(wù)器實(shí)現(xiàn)。

在部署應(yīng)用程序的方式上,Twisted通過TAC文件、插件以及命令行工具twistd的部署方式已經(jīng)獲得了成功。但是有趣的是,對(duì)于大多數(shù)大型Twisted應(yīng)用程序來說,部署它們?nèi)匀恍枰貙懸恍┻@類管理和監(jiān)控組件;Twisted的架構(gòu)并沒有對(duì)系統(tǒng)管理員的需求呈現(xiàn)出太多的友好性。這也反映了一個(gè)事實(shí),那就是對(duì)于系統(tǒng)管理員來說Twisted歷來就沒有太多架構(gòu)可言,而這些系統(tǒng)管理員才是部署和維護(hù)應(yīng)用程序的專家。在這方面,Twisted在未來架構(gòu)設(shè)計(jì)的決策上需要更積極的征求這類專家級(jí)用戶的反饋意見。

21.3 反思與教訓(xùn)

Twisted最近剛剛渡過了其10周年的誕辰。自項(xiàng)目成立以來,由于受2000年早期的網(wǎng)絡(luò)游戲啟發(fā),目前的Twisted已經(jīng)在很大程度上實(shí)現(xiàn)了作為一個(gè)可擴(kuò)展、跨平臺(tái)、事件驅(qū)動(dòng)的網(wǎng)絡(luò)引擎的目標(biāo)。Twisted廣泛使用于生產(chǎn)環(huán)境中,從Google、盧卡斯電影到Justin.TV以及Launchpad軟件協(xié)作平臺(tái)都有在使用。Twisted中的服務(wù)器端實(shí)現(xiàn)是多個(gè)開源軟件的核心,包括BuildBot、BitTorrent以及TahoeLAFS。

Twisted從最初開發(fā)到現(xiàn)在,其架構(gòu)已經(jīng)經(jīng)歷了幾次大的變動(dòng)。Deferred對(duì)象作為一個(gè)關(guān)鍵部分被增加了進(jìn)來。如前文所述,這是用來管理延后的結(jié)果以及相應(yīng)的回調(diào)鏈。

還有一個(gè)重要的部分被移除掉了,在目前的實(shí)現(xiàn)中已經(jīng)幾乎看不到任何影子了,這就是Twisted應(yīng)用持久化(Twisted Application Persistence)。

Twisted應(yīng)用持久化

Twisted應(yīng)用持久化(TAP)是指將應(yīng)用程序的配置和狀態(tài)保存在一個(gè)pickle中。要運(yùn)行采用了這種方案的應(yīng)用需要兩個(gè)步驟:

  1. 使用mktap工具創(chuàng)建一個(gè)代表該應(yīng)用的pickle(該工具現(xiàn)已廢棄不用)。

  2. 使用twistd命令行工具進(jìn)行unpickle操作,然后運(yùn)行該應(yīng)用。

這個(gè)過程是受Smalltalk images的啟發(fā),因?yàn)槲覀冇憛捘欠N臨時(shí)性的且難以使用的專用配置語言,不希望它們?cè)陧?xiàng)目中不斷擴(kuò)散。我們更希望在Python中表示配置的細(xì)節(jié)。

很快,TAP文件就引入了不必要的復(fù)雜性。修改Twisted中的類并不會(huì)使pickle中這些類的實(shí)例得到改變。在pickle對(duì)象上使用新版本的類方法或?qū)傩詴r(shí)可能會(huì)使整個(gè)應(yīng)用崩潰。因此“升級(jí)版”的概念得以引入,即將pickle對(duì)象升級(jí)到新的API版本。但這就會(huì)出現(xiàn)升級(jí)版本的矩陣化現(xiàn)象,出現(xiàn)各種不同版本的pickle對(duì)象,因此單元測(cè)試時(shí)需要維護(hù)涵蓋所有可能的升級(jí)路徑。想全面地跟蹤所有的接口變化依然很難,而且容易出錯(cuò)。

TAP以及相關(guān)的組件全部被廢除了,最終從Twisted中完全剔除掉。取而代之的是TAC文件和插件系統(tǒng)。TAP這個(gè)縮寫被重新定義為Twisted Application Plugin(Twisted應(yīng)用插件),如今已經(jīng)很難在Twisted中找到pickle系統(tǒng)的蹤跡了。

我們從TAP的慘敗中得到的教訓(xùn)是:如果可維護(hù)性要達(dá)到合理化的程度,則持久性數(shù)據(jù)就需要有一個(gè)明確的模式。更一般的是,我們學(xué)到了如何為項(xiàng)目增加復(fù)雜度:為了解決某個(gè)問題而需要引入一個(gè)新系統(tǒng)時(shí),我們要正確理解這個(gè)方案的復(fù)雜性,并經(jīng)過測(cè)試。新系統(tǒng)所帶來的價(jià)值應(yīng)該明顯大于其復(fù)雜性。確保了這一點(diǎn)之后我們才能將方案付諸于項(xiàng)目中。

web2:重構(gòu)的教訓(xùn)

雖然這基本上不屬于架構(gòu)設(shè)計(jì)上的決策,但從項(xiàng)目管理的角度來看,重寫Twisted的Web實(shí)現(xiàn)對(duì)于Twisted的外在形象以及維護(hù)者對(duì)代碼庫(kù)中其他部分做架構(gòu)改善的能力卻有著長(zhǎng)遠(yuǎn)的影響,因此這里值得我們簡(jiǎn)單討論一下。

在2000年中期,Twisted的開發(fā)者決定完全重寫twisted.web API,在Twisted代碼庫(kù)中將其作為一個(gè)單獨(dú)的項(xiàng)目實(shí)現(xiàn),這就是web2。web2將包含許多針對(duì)原有twisted.web的改善和提升,包括完全支持HTTP1.1,以及對(duì)流式數(shù)據(jù)的API支持。

web2最初只是試驗(yàn)性的項(xiàng)目,但最終被大型項(xiàng)目所采用,甚至意外的得以在Debian系統(tǒng)上打包發(fā)布。twisted.web和web2的開發(fā)一直并行持續(xù)了多年,新用戶常常被這兩個(gè)并行的項(xiàng)目搞混,關(guān)于究竟應(yīng)該使用哪種實(shí)現(xiàn)缺乏明確的提示,這使得新用戶很沮喪。轉(zhuǎn)換到web2的情況從未出現(xiàn),終于在2011年開發(fā)者將其從代碼庫(kù)中移除,官方主頁上再也看不到它了。web2中做出的一些改進(jìn)也被慢慢地移植回twisted.web中。

Twisted獲得了難以導(dǎo)航且結(jié)構(gòu)混亂,容易使新開發(fā)者感到困惑的“惡名”,這個(gè)印象部分歸功于web2。以至于數(shù)年之后,Twisted社區(qū)仍然在同這種不和諧的名聲做斗爭(zhēng)。

我們從web2中汲取的教訓(xùn)是:從頭開始重構(gòu)一個(gè)項(xiàng)目通常都是糟糕的主意。但如果必須這么做,請(qǐng)確保開發(fā)者社區(qū)能夠懂得這么做的長(zhǎng)遠(yuǎn)意義,而且在用戶社群中要有明確的選擇該使用哪種實(shí)現(xiàn)。

如果Twisted能夠倒退回web2的時(shí)代,開發(fā)者們應(yīng)該會(huì)對(duì)twisted.web做一系列向后兼容型的修改而不是去重構(gòu)。

緊跟互聯(lián)網(wǎng)的浪潮

我們使用互聯(lián)網(wǎng)的方式還在持續(xù)演進(jìn)中。把多種協(xié)議的實(shí)現(xiàn)作為軟件核心的一部分,這個(gè)技術(shù)決策使得Twisted背負(fù)了維護(hù)這些協(xié)議的沉重負(fù)擔(dān)。隨著標(biāo)準(zhǔn)的改變以及對(duì)新協(xié)議的采納,原有的實(shí)現(xiàn)必須跟著演進(jìn),同時(shí)需要嚴(yán)格的保證向后兼容性。

Twisted基本上是一個(gè)志愿者驅(qū)動(dòng)型的項(xiàng)目,項(xiàng)目發(fā)展的限制因素不是技術(shù)社區(qū)的熱情,而在于志愿者的時(shí)間。比如說,1999年的RFC 2616中定義了HTTP 1.1規(guī)范,而在Twisted的HTTP協(xié)議實(shí)現(xiàn)中增加對(duì)HTTP 1.1的支持卻在2005年才開始,等到完成時(shí)已經(jīng)是2009年了。1998年RFC 2460中定義了對(duì)IPv6的支持,而Twisted對(duì)其的支持還在進(jìn)行中,但是直到2011年都未能合并進(jìn)去。

隨著所支持的操作系統(tǒng)的接口改變,實(shí)現(xiàn)也要跟著演進(jìn)。比如,epoll事件通知機(jī)制是在2002年加入到Linux 2.5.44版中的,Twisted隨之也發(fā)展出基于epoll的reactor事件循環(huán)來利用這個(gè)新的系統(tǒng)接口。2007年時(shí),蘋果公司發(fā)布的OS 10.5 Leopard系統(tǒng)中,系統(tǒng)調(diào)用poll的實(shí)現(xiàn)居然不支持外設(shè),對(duì)于蘋果公司來說這個(gè)問題足以讓他們?cè)谙到y(tǒng)自帶的Python中屏蔽掉select.poll接口。Twisted不得不自行解決這個(gè)問題,并從那時(shí)起就對(duì)用戶提供文檔說明。

有時(shí)候,Twisted的開發(fā)并沒有緊跟網(wǎng)絡(luò)世界的變化,有一些改進(jìn)被移到核心層之外的程序庫(kù)中去了。比如Wokkel project,這是對(duì)Twisted的Jabber/XMPP支持的改進(jìn)合集,已經(jīng)作為“待合入”的獨(dú)立項(xiàng)目有幾年之久了,但還沒有看到合入的希望。在2009年也曾經(jīng)嘗試過增加WebSocket到Twisted中,因?yàn)闉g覽器已經(jīng)開始采納對(duì)新協(xié)議的支持了。但開發(fā)計(jì)劃最終卻轉(zhuǎn)到其他外部項(xiàng)目中去了,因?yàn)殚_發(fā)者們決定暫不包含新的協(xié)議,直到IETF把它從草案轉(zhuǎn)變成標(biāo)準(zhǔn)以后再說。

所有這一切都在說明,庫(kù)和附加組件的擴(kuò)散有力的證明了Twisted的靈活性和可擴(kuò)展性。通過采用嚴(yán)格的測(cè)試驅(qū)動(dòng)開發(fā)策略以及文檔化和編碼規(guī)范標(biāo)準(zhǔn),這樣做能夠幫助項(xiàng)目避免出現(xiàn)需要“回爐”的情況。在維護(hù)大量所支持的協(xié)議和平臺(tái)的同時(shí)保持向后兼容性。Twisted是一個(gè)成熟、穩(wěn)定的項(xiàng)目,并繼續(xù)保持有非?;钴S的開發(fā)狀態(tài)。

Twisted期待著在下一個(gè)十年里成為你遨游互聯(lián)網(wǎng)的引擎。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)