読者です 読者をやめる 読者になる 読者になる

非同期Webサーバーならばcycloneがよさげ

Python Twisted WebSocket

まだ深くソースコードを読めていないので「非同期Webサーバー」という言い方が正しいかどうかは自信がない。

cycloneはtornadoと非常によく似たAPIをもっている。例えば、お馴染みのHello Worldならば次の通り。

# -*- mode: python -*-
# helloworld.tac
import cyclone.web
from twisted.python import log
from twisted.internet import reactor
from twisted.application import service, internet

class MainHandler(cyclone.web.RequestHandler):
    def get(self):
        self.write("Hello World")

webapp = cyclone.web.Application([
        (r"/", MainHandler),
        ])

application = service.Application("cyclone")
cyclone = internet.TCPServer(8000, webapp)
cyclone.setServiceParent(application)

リクエストの処理部分はtornado風なのだが、非同期IOのイベントループの部分はTwistedを使っているのがcycloneの特徴。上のコードを動かすには、

$ twistd --reactor=epoll --nodaemon --python=helloworld.tac

のようにtwistdコマンドを使う。twistdを使うからdaemon化するのもの楽、tornadoと違ってreactorのバックエンドも変えられる、ってところが売りなんだと思う。Twistedも個人的はあんまり使ったことがないから正当な評価は下せないが、少なくとも、tornadoがcurlに依存しているよりは良いデザインだと思う。

素のTwistedより良いと思う理由は、TwistedのAPIアーキテクチャって取っ付きにくく、tornado/cyclone風のAPIの方が見通しが良いと思うし、そもそもTwistedのHTTPサポートってTwisted Webを使えばいいのか、Twisted Web2を使えばいいのかさっぱり分からないから。

あと、WebSocketのサポートがよさげ。簡潔に書けるなという印象がある。

ほとんどcycloneのWebSocketのデモコードそのままなのだが、必要最小限でWebSocketを使ったエコーサーバー風のアプリケーションを書くと次のような感じ。

# -*- mode: python -*-
# websocket.tac
import cyclone.web
from twisted.application import service, internet

class MainHandler(cyclone.web.RequestHandler):
    def get(self):
        self.render("interact.html")

class WebSocketHandler(cyclone.web.WebSocketHandler):
    def connectionMade(self, *args, **kwds):
        print "connection made:", args, kwds

    def messageReceived(self, message):
        self.sendMessage("echo %s" % message)

    def connectionLost(self, why):
        print "connection lost:", why

class WebSocketApplication(cyclone.web.Application):
    def __init__(self):
        handlers = [
            (r"/", MainHandler),
            (r"/websocket", WebSocketHandler),
            ]

        settings = { "template_path": "." }
        cyclone.web.Application.__init__(self, handlers, **settings)

application = service.Application("websocket")
internet.TCPServer(8000, WebSocketApplication()).setServiceParent(application)

途中に出てくるinteract.htmlというのは、長くなるので省略。以下と同じ。

ちなみにこのHTMLは、元をただせばJoe ArmstrongのWebSocketの例のHTMLと同じ。もっと前の作者がいるのかもしれないがそこまでは調べていない。