パート16: Twisted をデーモン化する

はじめに

これまで私たちが記述してきたサーバは、 print 文で画面に出力し、ターミナルウィンドウ内で動くだけでした。 開発版ではこれでも構いませんが、製品版としてサービスを配備することからは程遠い方法です。 お行儀の良いサーバ製品は次のようにあるできでしょう。

  1. ターミナルやユーザセッションと関連付くことなく、 daemon プロセスとして動きます。 管理者がログアウトしただけでサービスを終了させたくはないでしょうね。

  2. デバッグとエラー出力をローテートするログファイル一式か syslog に送ります。

  3. 余分な権限をそぎ落とします。たとえば、実行前に権限の低いユーザに切り替えるなどです。

  4. 管理者が簡単にデーモンへ シグナルを送信 できるように、 ファイルに pid を記録します。

Twisted が提供する twistd スクリプトを使うと、これら全ての機能を手に入れることができます。 しかしまずは、私たちのコードに手を加える必要があります。

コンセプト

twistd を理解するには、Twisted におけるいくつかのコンセプトを学習する必要があります。 もっとも大事なのは Service です。 これまでと同じように、新しいコンセプトには新しい Interface がつきものです。

IService

IService インターフェイスは開始と停止が可能な名前付きのサービスを定義します。 サービスとは何をするものなのでしょう? 何でも良いのです。 サービスの特定の関数を定義するのではなく、インターフェイスは汎用的な属性とメソッドの一式を提供する何かを要求するだけです。

ふたつの必須属性があります。 namerunning です。 name 属性は、 “fastpoetry” のような、単なる文字列です。 running 属性は真偽値で、サービスが問題なく開始したときは True になります。

IService のいくつかのメソッドに触れてみましょう。 明らかなものはスキップするとして、より上級者向けや簡単な Twisted プログラムではあまり使われないものを見てみましょう。 IService の根幹を成すふたつのメソッドは startServicestopService です。

def startService():
    """
    Start the service.
    """

def stopService():
    """
    Stop the service.

    @rtype: L{Deferred}
    @return: a L{Deferred} which is triggered when the service has
        finished shutting down. If shutting down is immediate, a
        value can be returned (usually, C{None}).
    """

さて、これらのメソッドの実際の動作は、対象となるサービスに依存するでしょう。 たとえば、 startService メソッドは次のことを行うかもしれません。

  • 設定情報を読み込みます。もしくは、

  • データベースを初期化します。もしくは、

  • ポートを待ち受け始めます。もしくは、

  • まったく何もしません。

そして stopService には次の可能性があります。

  • データを永続化します。もしくは、

  • 有効なデータベース接続を閉じます。もしくは、

  • ポートで待ち受けるのを止めます。もしくは、

  • まったく何もしません。

カスタムサービスを記述するときは、これらのメソッドを正確に実装する必要があります。 しかし、ポートを待ち受けるようないくつかの共通する振る舞いには、私たちが利用できるサービスを Twisted が提供してくれます。

stopService は遅延オブジェクトを返すかもしれないことに気をつけてください。 これは、サービスが完全に停止したら発火するために必要とされます。 これにより、アプリケーション全体が停止する前に、サービス停止後に自分自身のクリーンナップが終了できるようにします。 サービスがすぐさまに停止するなら、遅延オブジェクトの替わりに単に None を返せば良いでしょう。

サービスは、一緒に開始および終了されるような集合として構成されます。 ここで見ておく最後の IService メソッドは setServiceParent で、 これはサービスを集合に追加します。

def setServiceParent(parent):
    """
    Set the parent of the service.

    @type parent: L{IServiceCollection}
    @raise RuntimeError: Raised if the service already has a parent
        or if the service has a name and the parent already has a child
        by that name.
    """

いかなるサービスも親を持つことができます。 これはサービスが階層構造を持って構成されることを意味します。 そして、次に見ていく Interface をもたらします。

IServiceCollection

IServiceCollection インターフェイスは IService オブジェクトを包含可能なオブジェクトを定義します。 サービスの集合は、以下のメソッドを持つ単なるコンテナクラスなのです。

  • 名前からサービスを探し出します。 (getServiceNamed)

  • 集合に含まれるサービスに対して繰り返します。 (__iter__)

  • 集合にサービスを追加します。 (addService)

  • 集合からサービスを取り除きます。 (removeService)

IServiceCollection の実装が自動的に IService の実装であるわけではないことに気をつけましょう。 しかし、あるクラスが両方のインターフェイスを実装できない理由はありません (少し先で例を見ていきましょう) 。

Application

Twisted の Application は分離されたインターフェイスとして実装されているわけではありません。 むしろ Application オブジェクトは、ここで取り扱う範囲外のほかのいくつかのインターフェイスと同様に、 IServiceIServiceCollection の両方を実装するために必要とされます。

Application は、Twisted アプリケーション全体を表現するトップレベルのサービスです。 デーモンにおける全てのサービスは Application オブジェクトの子供 (もしくは孫や子孫) になるでしょう。

自分自身で Application を実際に実装することは稀です。 私たちが使うような実装を Twisted が提供してくれます。

Twisted Logging

Twisted は twisted.python.log モジュールに独自のロギング方法を持ちます。 ログを書き込む基本的な API は簡単ですので、 basic-twisted/log.py にある簡単な例を見せるだけにしましょう。 興味を持ったら Twisted のモジュールをよく読んでみてください。

わざわざロギングのハンドラをインストールするための API を示すことはしません。 twistd がやってくれるからです。

FastPoetry 2.0

それではコードを見ていきましょう。 早い詩のサーバを twistd で動かすように更新しました。 ソースコードは twisted-server-3/fastpoetry.py にあります。 まずは poetry protocol があります。

class PoetryProtocol(Protocol):

    def connectionMade(self):
        poem = self.factory.service.poem
        log.msg('sending %d bytes of poetry to %s'
                % (len(poem), self.transport.getPeer()))
        self.transport.write(poem)
        self.transport.loseConnection()

それぞれの新しい接続を記録するために print 文ではなく twisted.python.log.msg 関数を使っていることに注意してください。 factory クラス は次のようになります。

class PoetryFactory(ServerFactory):

    protocol = PoetryProtocol

    def __init__(self, service):
        self.service = service

お分かりのように、詩はもはやファクトリに保存されず、ファクトリから参照されるサービスオブジェクトに保存されます。 プロトコルが、ファクトリ経由でサービスから詩を取得する方法に注意してください。 最後に、 サービスクラス自身 を見てみましょう。

class PoetryService(service.Service):

    def __init__(self, poetry_file):
        self.poetry_file = poetry_file

    def startService(self):
        service.Service.startService(self)
        self.poem = open(self.poetry_file).read()
        log.msg('loaded a poem from: %s' % (self.poetry_file,))

他の多くの Interface クラスのように、Twisted は私たちが独自実装を作るために使える基底クラスを提供します。これは役に立つデフォルトの振る舞いを持ちます。 ここでは PoetryService を実装するために twisted.application.service.Service クラスを使います。

基底クラスはすべての必須メソッドにおいてデフォルト実装を提供しますので、独自の振る舞いだけを実装すれば構いません。 ここでは詩のファイルを読み込むために startService を上書き (override) するだけです。 とはいえ基底クラスのメソッド (running 属性を設定してくれます) を呼び出していることに注意してください。

言及しておく価値のあることがもう一点あります。 PoetryService オブジェクトは PoetryProtocol の詳細について何も知りません。 サービスがすべきことは、詩を読み込んでそれを必要とするすべてのオブジェクトにアクセス手段を提供するだけです。 言い換えると PoetryService は、TCP 接続を介して詩を送信するような低レベルの詳しいことよりは、詩を提供するための高レベルの詳しいことに関心があるのです。 このため、たとえば UDP や XML-RPC のような他のプロトコルでも同じサービスを利用できます。 私たちの単純なサービスでは小さなことですが、もっと現実的なサービスの実装ではアドバンテージを想像できるでしょう。

これが典型的な Twisted プログラムだったとすれば、これまで見てきた全てのコードは実際にはこのファイルには存在しません。 むしろ他のモジュール (fastpoetry.protocolfastpoetry.service) にあるべきでしょう。 しかし、例が自己包括になるように作ってきた方法に従い、単一のスクリプトにすべてを含めるようにしてきました。

Twisted tac ファイル

スクリプトの残りの部分は通常はコンテンツ全体を含んでいます。 Twisted の tac ファイルです。 tac ファイルは twistd にアプリケーション構築方法を伝えるための Twisted アプリケーションの設定ファイルです。 設定ファイルですので、設定を選択すること (ポート番号や詩のファイル置き場など) は特定の方法でアプリケーションを実行することに責任を持ちます。 言い換えると、 tac ファイルは、いかなる詩のサーバでも起動できるような汎用的なスクリプトというよりは、 サービスにおける特定の配備を表現します (“あの”詩を”この”ポートで提供する)。

複数の詩のサーバを同じホスト上で実行するなら、それぞれに対して tac ファイルを用意することになります (tac ファイルが汎用的な目的のコードを含まない理由を理解して頂けるでしょうか)。 私たちの例では、 tac ファイルは poetry/ecstasy.txt をループバックインターフェイスの 10000 番ポートで提供するように設定されています。

# configuration parameters
port = 10000
iface = 'localhost'
poetry_file = 'poetry/ecstasy.txt'

twistd はこれらの特定の変数に関して何も知らない、ということに注意しましょう。 設定値を一ヶ所にまとめておくために定義しているだけです。 実際には、ちょっと先で見ていくように、 twistd はファイル全体でひとつの変数を管理しているだけです。 次に、アプリケーションを構築していくことを 始めましょう

# this will hold the services that combine to form the poetry server
top_service = service.MultiService()

私たちの詩のサーバはふたつのサービスから構成されます。上で定義した PoetryService と、詩を送り出すために待ち受けるソケットを生成する Twisted 組み込みのサービスです。 これらふたつのサービスはお互いにはっきりと関連付いていますので、 MultiService を使ってグループ化しておきましょう。 MultiServiceIServiceIServiceCollection の両方を実装しているTwisted のクラスです。

サービスの集合ですから MultiService はふたつの詩のサービスをグループ化してくれます。 サービスでもありますので、 MultiService 自身が開始されると子供となるサーバの起動もやってくれますし、停止するときはそれらも停止させます。 ひとつ目の詩のサービスを集合に 追加 してみましょう。

# the poetry service holds the poem. it will load the poem when it is
# started
poetry_service = PoetryService(poetry_file)
poetry_service.setServiceParent(top_service)

これは非常に簡単なことです。 PoetryService を作成して、 setServiceParent で集合に追加するだけです。 このメソッドは Twisted の基底クラスから継承しています。 次は TCP リスナーを 加えましょう

# the tcp service connects the factory to a listening socket. it will
# create the listening socket when it is started
factory = PoetryFactory(poetry_service)
tcp_service = internet.TCPServer(port, factory, interface=iface)
tcp_service.setServiceParent(top_service)

Twisted は、特定のファクトリ (今の場合は PoetryFactory) に接続して TCP を待ち受けるソケットを生成するために、 TCPServer サービスを提供します。 tac ファイルの仕事でアプリケーションは開始できる状態 (実際に開始はしませんが) になりますので、 reactor.listenTCP を直接呼び出すことはしません。 twistd によって開始されると TCPServer はソケットを生成するでしょう。

ここまでで、サービスに名前を付けていないことに気付いたかもしれません。 サービスに名前を付けることは必須事項ではなく、実行時に探し出したい (“look up”) ならそうするべき、というオプション機能です。 今の小さなアプリケーションではその必要はありませんので、ここでは取り上げません。

ようやくふたつのサービスを集合にまとめることができましたね。 Application を作って集合に 追加します

# this variable has to be named 'application'
application = service.Application("fastpoetry")

# this hooks the collection we made to the application
top_service.setServiceParent(application)

このスクリプト内で twistd が本当に注意する唯一の変数は application だけです。 twistd は開始されるべきであるアプリケーションを見つけるでしょう (変数は “application” という名前でなくてはなりません)。 アプリケーションが開始されると、追加しておいた全てのサービスも同様に開始されるでしょう。

図34で、ここまでで作ったアプリケーションの構造を示します。

_images/p16_application.png

図34: fastpoetry アプリケーションの構造

サーバを動かす

新しいサーバを動かしてみましょう。 tac ファイルを参照し、 twistd を使って開始させます。 もちろん、これも普通の Python ファイルです。 まずは Python を使って実行して何が起こるかを見てみます。

python twisted-server-3/fastpoetry.py

やってみると、何も起こらないことがわかるでしょう! 先ほど書いたように、 tac ファイルの仕事はアプリケーションを実行可能な状態にすることで、実際に実行はしません。 こうした tac ファイルの特別な目的を覚えておくために、拡張子に .py ではなく .tac を使う人もいます。 しかし twistd スクリプトは拡張子に関して気にしません。

それでは twistd を使って実際にサーバを動かしてみましょう。

twistd --nodaemon --python twisted-server-3/fastpoetry.py

このコマンドを実行すると、次のような出力を得られるでしょう。

2010-06-23 20:57:14-0700 [-] Log opened.
2010-06-23 20:57:14-0700 [-] twistd 10.0.0 (/usr/bin/python 2.6.5) starting up.
2010-06-23 20:57:14-0700 [-] reactor class: twisted.internet.selectreactor.SelectReactor.
2010-06-23 20:57:14-0700 [-] __builtin__.PoetryFactory starting on 10000
2010-06-23 20:57:14-0700 [-] Starting factory <__builtin__.PoetryFactory instance at 0x14ae8c0>
2010-06-23 20:57:14-0700 [-] loaded a poem from: poetry/ecstasy.txt

いくつか気にかけておくことがあります。

  • Twisted のロギングシステムからの出力が見えます。 PoetryFactorylog.msg を呼び出すことも含みます。 tac ファイルでロガーをインストールしていませんので、 twistd がインストールしてくれました。

  • ふたつのメインサービスもあり、 PoetryServiceTCPServer が起動しています。

  • シェルプロンプトは決して制御を戻しません。 サーバがデーモンとして動作していないことを意味します。 デフォルトでは、 twistd はデーモンプロセスとしてサーバを実行します (それこそが twistd の主要な存在理由ですから) が、 --nodaemon オプションを付けると twistd はサーバを通常のシェルプロセスとして実行し、標準出力にログを出力します。 この挙動は tac ファイルをデバッグするときに便利です。

それでは詩を取得してサーバをテストしてみましょう。 これまでに作ってきた詩のクライアントでも、単に netcat を使っても構いません。

netcat localhost 10000

サーバから取得して、次のような新しいログが出力されるでしょうか。

2010-06-27 22:17:39-0700 [__builtin__.PoetryFactory] sending 3003 bytes of poetry to IPv4Address(TCP, '127.0.0.1', 58208)

これは PoetryProtocol.connectionMadelog.msg の呼び出しによるものです。 サーバにもっとリクエストを発行してみると、それぞれのリクエストごとにログが出力されます。

ではここで Ctrl+C を押してサーバを停止させましょう。 次のような出力があるはずです。

^C2010-06-29 21:32:59-0700 [-] Received SIGINT, shutting down.
2010-06-29 21:32:59-0700 [-] (Port 10000 Closed)
2010-06-29 21:32:59-0700 [-] Stopping factory <__builtin__.PoetryFactory instance at 0x28d38c0>
2010-06-29 21:32:59-0700 [-] Main loop terminated.
2010-06-29 21:32:59-0700 [-] Server Shut Down.

お分かりのように、Twisted はクラッシュしません。 きれいに自分自身を停止させ、ログメッセージでそれに関して教えてくれます。 ふたつのメインサービスも同じように停止しています。

もう一度サーバを起動させましょう。

twistd --nodaemon --python twisted-server-3/fastpoetry.py

シェルをもう一つ開いて twisted-intro ディレクトリに移動してください。 ディレクトリ一覧に twistd.pid ファイルがあるはずです。 このファイルは twistd によって作成され、起動中のサーバのプロセス ID が保存されています。 サーバを停止させるために次のコマンドを実行してみましょう。

kill `cat twistd.pid`

サーバを停止するときに、 twistd がプロセス ID のファイルを削除してくれます。

現実のデーモン

サーバを実際のデーモンプロセスとして実行させましょう。 twistd のデフォルトの振る舞いですので、むしろ簡単です。

twistd --python twisted-server-3/fastpoetry.py

今回はシェルのプロンプトはすぐさま処理を返してきましたね。 ディレクトリの一覧を表示させてみると、実行させているサーバの twistd.pid ファイルに加えて、 先ほどまではシェルプロンプトに表示されていたログが書き込まれる twistd.log ファイルもあるはずです。

デーモンプロセスを開始させると、 twistd は標準出力ではなくログファイルに書く込むログハンドラーをインストールします。 デフォルトのログファイルは twistd.log で、 twistd を実行させた場所と同じディレクトリです。 しかし、必要なら --logfile オプションを使って変更することもできます。 twistd がインストールするハンドラーはログファイルのサイズが 1MB を超過する度にログローテートもやってくれます。

システム上の全てのプロセスを一覧表示することで、実行中のサーバを見えるようにしておくべきでしょう。 もうひとつの詩を取得することでサーバをテストしてみましょう。 リクエストしたそれぞれの詩に対してログファイルに新しいエントリが追加されますよね。

サーバはもはやシェルに繋がっていません (init を除くいかなるものともです) ので、 Ctrl-C では停止させることができません。 きちんとしたデーモンプロセスですので、あなたがログアウトしても動き続けてくれるでしょう。 しかし、 twistd.pid ファイルを使ってプロセスを停止させることはできます。

kill `cat twistd.pid`

ログに停止メッセージが書き込まれると、 twistd.pid ファイルが削除され、サーバが止まります。やりましたね。

twistd のその他の起動オプションを確認しておくのは良い考えですね。 たとえば、デーモンを実行させる前に異なるユーザーやグループのアカウントに切り替わるよう twistd に伝えることがあります (典型的には、セキュリティ警告により、サーバが必要としない権限をそぎ落とすことがあります)。 他のオプションを見ていくのは退屈なので、 twistd--help オプションを渡して確認しておいてください。

Twisted のプラグイン機構

よし、それではサーバをまっとうなデーモンプロセスとして起動するために twistd を使いましょう。 すべてがよくできており、設定ファイルが本当に Python のソースファイルであるということで、設定には非常な柔軟さがあります。 とはいえ、いつも多大なる柔軟性が必要なわけでもありません。 私たちの詩のサーバにとっては、注意すべきは 2, 3 のオプションだけでしょう。

  • 提供する詩

  • 提供するためのポート番号

  • 待ち受けるインターフェイス

これらの値ごとに簡単なバリエーションの新しい tac ファイルを作るのは、ちょっとやりすぎ感があります。 twistd のコマンドラインにオプションとして指定できると良さそうですね。 これは Twisted のプラグインシステムで実現できます。

Twisted のプラグインは名前を付けられた Application を定義する方法を提供してくれます。 twistd が動的に発見して実行できるようなコマンドラインオプションのセットも一緒です。 Twisted 自体にも組み込みプラグインのセットがあります。 twistd に引数を何もつけずに実行すると見ることができます。 twisted-intro ディレクトリの外で、とりあえずやってみましょう。 ヘルプ部分の後ろに、次のような出力が見えますよね。

...
ftp                An FTP server.
telnet             A simple, telnet-based remote debugging service.
socks              A SOCKSv4 proxy service.
...

それぞれの行は Twisted の組み込みプラグイン を表します。 twistd を使ってどれでも起動できます。 それぞれのプラグインは独自のオプションを持ち、 --help を使うと見つけられます。 ftp プラグインのオプションを見てみましょう。

twistd ftp --help

twistd 自身ではなく ftp プラグインのオプションですので、 ftp コマンドの後に --help オプションを配置していることに気をつけてください。 詩のサーバを動かしたように、 twistd を使って ftp サーバを動かすことができます。 プラグインですから名前を指定するだけで動かせます。

twistd --nodaemon ftp --port 10001

このコマンドは ftp プラグインをデーモンではないモードで 10001 番ポートで動かします。 プラグイン特有のオプションである port はプラグイン名の後ですが、 twistdnodaemon オプションはプラグイン名の前であることに気をつけてください。 詩のサーバでもやってみたように、 Ctrl-C でプラグインを停止できます。

それでは、私たちの詩のサーバを Twisted プラグイン化してみましょう。 まずは 2, 3 の新しいコンセプトを紹介します。

IPlugin

あらゆる Twisted プラグインは twisted.plugin.IPlugin インターフェイスを実装しなくてはなりません。 Interface の宣言を見てみると、実際には何のメソッドも定義していないことが分かるでしょう。 IPlugin を実装することは、 twistd が見つけられるように、 プラグインが「やぁ、自分はプラグインだよ!」と主張する簡単な方法です。 もちろん、何かの用途で他のインターフェイスを実装しなくてはならないでしょうし、少し先でやってみます。

しかし、オブジェクトが空っぽのインターフェイスを実際に実装しているかを知るにはどうしましょうか? zope.interface パッケージには、特定のクラスが特定のインターフェイスを実装していると宣言するために使う implements と呼ばれる関数があります。 詩のサーバのプラグインバージョンで例を見ていきましょう。

IServiceMaker

私たちのプラグインは、 IPlugin に加えて IServiceMaker インターフェイスも実装します。 IServiceMaker を実装しているオブジェクトはアプリケーションを実行する上で核となる IService の生成方法を知っています。 IServiceMaker には三つの属性とひとつのメソッドを指定します。

  • tapname: プラグインのための文字列名です。”tap” は Twisted Application Plugin を意味します。 Note: 古いバージョンの Twisted は “tapfiles” と呼ばれるピックル化されたアプリケーションファイルも使っていました。しかし、この機能は廃止されました。

  • description: プラグインの説明です。ヘルプテキストの一部として twistd が表示します。

  • options: このプラグインが受け付けるコマンドラインオプションを説明するオブジェクトです。

  • makeService: 新しい IService オブジェクトを生成するメソッドです。メソッドには特定のコマンドラインオプションのセットが与えられます。

次のバージョンの詩のサーバで、これらをまとめる方法をみていきましょう。

Fast Poetry 3.0

Fast Poetry のプラグインバージョンに目を向ける準備が整いましたね。 twisted/plugins/fastpoetry_plugin.py にあります。

これまでの例とは異なるディレクトリ名であることに気付いたでしょうか。 twistd が、Python モジュールの検索パスから見て twisted/plugins ディレクトリにあるプラグインファイルを要求するためです。 ディレクトリはパッケージである必要はありません (つまり __init__.py ファイルは不要です) し、 パス上に複数の twisted/plugins ディレクトリがあっても構いません。 twistd はそれらのすべてを見つけてくれるでしょう。 プラグインのために使う実際のファイル名はどちらでも構いません。 しかし、アプリケーションが実現することを表す名前にしておくべきでしょう。

プラグインの最初の部分は tac ファイルの時と同じく、詩のプロトコルとファクトリ、そしてサービスの実装を含んでいます。 なお先ほどとと同じように、このコードは通常は異なるモジュールにあるべきでしょうが、サンプルコードがひとつにまとまるように、プラグインにまとめています。

次にプラグインのコマンドラインオプションの 宣言 があります。

class Options(usage.Options):

    optParameters = [
        ['port', 'p', 10000, 'The port number to listen on.'],
        ['poem', None, None, 'The file containing the poem.'],
        ['iface', None, 'localhost', 'The interface to listen on.'],
        ]

このコードは、 twistd コマンドラインでユーザーがプラグイン名の後ろに指定できるプラグイン特有のオプションを定義します。 何をやっているか比較的明らかなので、ここで深くは見ていきません。 プラグインの中心となるコードに進んでいきましょう。 service maker クラス です。

class PoetryServiceMaker(object):

    implements(service.IServiceMaker, IPlugin)

    tapname = "fastpoetry"
    description = "A fast poetry service."
    options = Options

    def makeService(self, options):
        top_service = service.MultiService()

        poetry_service = PoetryService(options['poem'])
        poetry_service.setServiceParent(top_service)

        factory = PoetryFactory(poetry_service)
        tcp_service = internet.TCPServer(int(options['port']), factory,
                                         interface=options['iface'])
        tcp_service.setServiceParent(top_service)

        return top_service

このコードでは、 私たちのクラスが IServiceMakerIPlugin の両方を実装する、と宣言するために zope.interface.implements 関数を使う方法が分かりますね。

以前の tac ファイル実装から makeService のコードを見つけられるでしょう。 しかし、今回は自分たちで Application オブジェクトを作成する必要がありません。 アプリケーションが起動できるようにトップレベルのサービスを生成して返してあげるだけで良いのです。 残りは twistd がやってくれます。 twistd に与えられたプラグイン特有のコマンドラインオプションを取得するために options 引数を使っていることに注意してください。

そのクラスを宣言した後に やるべきこと は次の一行だけです。

service_maker = PoetryServiceMaker()

twistd スクリプトはプラグインのインスタンスを見つけて、トップレベルサービスを構築するために使うでしょう。 tac ファイルとは異なり、変数名は取るに足らないものです。 私たちのオブジェクトが IPluginIServiceMaker の両方を実装していること次第です。

自分たちのプラグインを作成しましたので、動かしてみましょう。 twisted-intro ディレクトリにいるか、 twisted-intro ディレクトリが Python モジュールの検索パスに含まれていることを確認してくださいね。 そうしたら twistd 自身を実行しましょう。 プラグイン一覧のひとつに “fastpoetry” が見えるでしょうか。プラグインファイルに書いた説明文も表示されますよね。

twisted/plugins ディレクトリに dropin.cache と呼ばれる新しいファイルがあることにも気付きましたか。 このファイルはプラグインが引き続いてスキャンするのを高速化するために twistd によって生成されます。

よし、プラグインのヘルプを表示させましょう。

twistd fastpoetry --help

ヘルプ文には fastpoetry プラグイン特有のオプションもあるでしょう。 最後に、私たちが作ったプラグインを実行させましょう。

twistd fastpoetry --port 10000 --poem poetry/ecstasy.txt

これで fastpoetry サーバがデーモンとして動き出します。 先ほどと同じように、カレントディレクトリに twistd.pidtwistd.log の両方のファイルがあるでしょう。 サーバをテストし終わったら停止させましょう。

kill `cat twistd.pid`

これで Twisted プラグインの作り方が分かりましたね。

まとめ

このパートでは Twisted サーバをデーモンに変換することについて学びました。 Twisted のロギングシステムと、 twistd を使って Twisted アプリケーションをデーモンプロセスとして起動する方法にも触れました。 デーモンの設定は tac 設定ファイルか Twisted プラグインのどちらかから読み取ります。 “パート17: 「コールバック」ではない方法”では、非同期プログラミングのより本質的なトピックに戻り、Twisted でコールバック群を構成するもう一つの方法を見ていきましょう。

おすすめの練習問題

  1. ふたつ目の詩を異なるポートで提供するように tac ファイルを修正してみましょう。 異なる MultiService オブジェクトを使うことにより、それぞれの詩のためのサービスは別々にしてください。

  2. 詩のプロキシサーバを開始させるための新しい tac ファイルを作ってみましょう。

  3. オプションとして二つ目の詩のファイルを受け付けて、それは二つ目のポートで提供するようにプラグインファイルを修正してみましょう。

  4. 詩のプロキシサーバ用に新しいプラグインファイルを作ってみましょう。