2009年07月24日

外部から停止可能なXMLRPCサーバ(Python2.X用)


taken by saschaaa (under CC)

XML RPCで最近良く遊んでいます。ぱっとサーバを立ち上げて、インタラクティブモードでクライアントを起動して、すぐにメソッドを呼び出したり、すごく便利です。dRubyみたいに、クライアントとサーバの区別なく使えて、コールバックもできて、オブジェクトも渡したりできるような仕組みと比べると低レベルですが、ちょっとした通信では結構便利です。

ですが、標準のSimpleXMLRPCServerを使って分かったのは、Djangoのテストサーバの偉いところです。Ctrl+Cを押すとその場でぱっと止まってくれます。でも、自分でSimpleHTTPServer, SimpleXMLRPCServerなんかを起動すると、止まってくれません。気軽にインタラクティブモードで起動したり閉じたりするのには不便です。Djangoは別にプロセスをあげてそこで起動しているため、メインプロセス側のKeyboardInterruptをうまく捕まえて処理できているようです。では、通常のサーバではそこまでやらなければならないのか、というと、別の方法があります。下記のサイトを参考にコードを書いてみました。

それでは、作った物を公開します。

# -*- coding: utf_8 -*-

"""外部から停止できるXMLRPCServer"""

import select
import threading
from SimpleXMLRPCServer import SimpleXMLRPCServer


class StoppableXMLRPCServer(SimpleXMLRPCServer):
   def __init__(self, *args, **kwargs):
       SimpleXMLRPCServer.__init__(self, *args, **kwargs)
       self.__serving = False
       self.__thread = None
       self.__is_shut_down = threading.Event()

   def serve_forever(self, in_thread=False):
       def serve_thread(server):
           server.serve_forever()
       if in_thread:
           self.__thread = threading.Thread(target=serve_thread, args=[self])
           self.__thread.start()
       else:
           self.__serving = True
           self.__is_shut_down.clear()
           while self.__serving:
               self.handle_request()
           self.__is_shut_down.set()

   def get_request(self):
       while True:
           r = select.select([self.socket], [], [], 1)
           if len(r[0]) > 0:
               return self.socket.accept()
           if not self.__serving:
               return (None, None)

   def verify_request(self, request, client_address):
       return request is not None

   def shutdown(self):
       if not self.__serving:
           return
       self.__is_shut_down.wait()
       self.__serving = False
       if self.__thread:
           self.__thread.join()
           self.__thread = None

if __name__ == "__main__":
    def add(a, b):
        return a + b
    server = StoppableXMLRPCServer(("localhost", 8000))
    server.register_function(add)
    server.serve_forever()

止め方としては、2種類用意しています。

  • serve_forever()で起動した後に、Ctrl+Cで止める
  • 別スレッドで立ち上げておいて、オブジェクト.shutdown()をコール

起動の仕方も2種類用意しています。通常の、ブロックされてしまう、serve_forever()のコールと、serve_forever(in_thread=True)として起動する方法です。後者でサーバを立ち上げると、処理はすぐに戻ってくるようになります。内部で別スレッドを起動し、その中でサーバを立ち上げます。XML RPCサーバをWindowsのサービスにする場合など、外部から処理のスタートや停止をするような場合にはこれができた方が便利かな、と思って実装してみました。

ちなみに、別スレッドで起動というのは僕がオリジナルで入れたコードですが、それ以外の物に関しては、Python3.0のコードを見たら既に対応済みでした。shutdown()というメソッド名なども、Python3.0にあわせてあります。ということで、このコードはPython2系限定(正確には2.5でしかテストしてない)です。

次はクライアント側のServerProxyの方かな。相手がいないと固まってしまう問題がなんとかできたらいいな、と思ってます。これからコード読もう。

posted by @shibukawa at 20:07 | Comment(64) | TrackBack(0) | Python はてなブックマーク - 外部から停止可能なXMLRPCサーバ(Python2.X用)
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/30794680
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック
検索ボックス

Twitter

www.flickr.com
This is a Flickr badge showing public photos and videos from shibukawa.yoshiki. Make your own badge here.
<< 2019年02月 >>
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28    
最近の記事
カテゴリ
過去ログ
Powered by さくらのブログ