プロセス管理ツールの supervisor のソースコードを読んでいると、サーバー(supervisord)とクライアント(supervisorctl)が XML-RPC を INET ドメインソケットではなく UNIX ドメインソケットで通信していることに気づいた。
ソースコードを読んで Python での実装を確認してみた。
supervisor 起動後の状態
supervisord デーモン起動後 Unix ドメインソケットでサーバーが LISTEN されていることを確認。
$ netstat -l --protocol=unix Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node Path unix 2 [ ACC ] STREAM LISTENING 5828 @/com/ubuntu/upstart unix 2 [ ACC ] STREAM LISTENING 7020 /var/run/dbus/system_bus_socket unix 2 [ ACC ] SEQPACKET LISTENING 5957 @/org/kernel/udev/udevd unix 2 [ ACC ] STREAM LISTENING 37510 /tmp/supervisor.sock.14587
クライアントプログラム
クライアントの XML-RPC over UNIX ドメインソケットは supervisor/xmlrpc.py で実装されている。そこから supervisor モジュールへの依存など不要なものを取っ払ったのが以下。
# Based On supervisor/xmlrpc.py import httplib import socket import xmlrpclib class UnixStreamHTTPConnection(httplib.HTTPConnection): def connect(self): self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(self.socketfile) class SupervisorTransport(xmlrpclib.Transport): def __init__(self, username=None, password=None, serverurl=None): conn = UnixStreamHTTPConnection('localhost') conn.socketfile = serverurl self.connection = conn self.headers = { "User-Agent" : 'Dummy', "Content-Type" : "text/xml", "Accept" : "text/xml" } def request(self, host, handler, request_body, verbose=0): self.headers["Content-Length"] = str(len(request_body)) self.connection.request('POST', handler, request_body, self.headers) r = self.connection.getresponse() data = r.read() return data def test_call_transport_directly(): server = SupervisorTransport('/tmp/supervisor.sock') handler = '/RPC2' data = xmlrpclib.dumps((), 'supervisor.getAPIVersion') print server.request(handler, data) def test(): proxy = SupervisorTransport(serverurl='/tmp/supervisor.sock') server = xmlrpclib.Server('http://127.0.0.1', transport=proxy) print server.supervisor.getState() if __name__ == '__main__': test() test_call_transport_directly()
XML-RPC サーバーを指定はするが、実際の通信は Transport を介して UNIX ドメインソケットに接続し、データのリクエスト/レスポンスも同ソケット経由で行うようにする。
あとは Unix ドメインソケットのインターフェースを HTTP に合わせてやればOK。
xmlrpclib.Transport の使い方の勉強になる。
サーバプログラム
サーバも UNIX ドメインソケットに対応しようとすると、次の Twisted-Python のメーリングリストにもあるように、ソケットファイルも URL スキームもパスを スラッシュ(/)で区切っているため、かなりグダグダな実装になる。
[Twisted-Python] XML-RPC over Unix Sockets?
http://twistedmatrix.com/pipermail/twisted-python/2009-July/019915.html
自分の能力ではコンパクトに実装できないので、実装は保留。