Pythonによる
Echoサーバのサンプル実装
@ttsubo
2013.9.23
<勉強メモ>
1
1. ブロッキングI/OなEchoサーバ
aaa
aaa
Echoサーバ
クライアント1
$ telnet 127.0.0.1 4000
Trying 127.0.0.1...
Connected to localhost.
Escape ch...
#!/usr/bin/env python
from socket import *
from time import ctime
HOST = '127.0.0.1' # localhost
PORT = 4000 # choose a ra...
2. ブロッキングI/OなEchoサーバ
aaa
aaa
Echoサーバ
クライアント1
$ telnet 127.0.0.1 4000
Trying 127.0.0.1...
Connected to localhost.
Escape ch...
#!/usr/bin/env python
import threading
from socket import *
from time import ctime
HOST = '127.0.0.1' # localhost
PORT = 4...
ユーザからの処理をブロッキングI/Oで処理する場合には、
マルチスレッドでアプリケーションを作成する必要があ
り、スレッド間の同期の問題などが発生するので、細心の
注意が必要になる。
ノンブロッキングI/Oで処理する場合には、1つのスレッド
で...
eventletとは
Eventlet is built around the concept of green threads (i.e. coroutines, we use the
terms interchangeably) that ...
3. ノンブロッキングI/OなEchoサーバ
aaa
aaa
Echoサーバ
クライアント1
$ telnet 127.0.0.1 4000
Trying 127.0.0.1...
Connected to localhost.
Escape ...
#!/usr/bin/env python
import eventlet; eventlet.monkey_patch()
import threading
from socket import *
from time import ctim...
4. ノンブロッキングI/OなEchoサーバ
aaa
aaa
Echoサーバ
クライアント1
$ telnet 127.0.0.1 4000
Trying 127.0.0.1...
Connected to localhost.
Escape ...
#!/usr/bin/env python
import eventlet
from time import ctime
HOST = '127.0.0.1' # localhost
PORT = 4000 # choose a random ...
5. ノンブロッキングI/OなEchoサーバ
aaa
aaa
Echoサーバ
クライアント1
$ telnet 127.0.0.1 4000
Trying 127.0.0.1...
Connected to localhost.
Escape ...
#!/usr/bin/env python
import eventlet
from time import ctime
HOST = '127.0.0.1' # localhost
PORT = 4000 # choose a random ...
ノンブロッリングI/OをEventlet活用して
Echoサーバを実装してみた。
感覚的には、シングルスレッド的に実装で
きるところが、Good !!
あと、ネイティブスレッドと異なり、
グリーンスレッドは、ユーザ空間で動作する
ので、グリーン...
Upcoming SlideShare
Loading in …5
×

Echo server implementation for Python

1,553 views
1,397 views

Published on

PythonによるEchoサーバのサンプル実装
〜eventletを活用したノンブロッキングI/O勉強メモ〜

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,553
On SlideShare
0
From Embeds
0
Number of Embeds
42
Actions
Shares
0
Downloads
25
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Echo server implementation for Python

  1. 1. Pythonによる Echoサーバのサンプル実装 @ttsubo 2013.9.23 <勉強メモ> 1
  2. 2. 1. ブロッキングI/OなEchoサーバ aaa aaa Echoサーバ クライアント1 $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 14:37:28 2013] aaa aaaaa [Mon Sep 23 14:37:31 2013] aaaaa aa [Mon Sep 23 14:37:32 2013] aa クライアント2 aaa $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa シングル スレッド 待ち処理となる シングルスレッドでEchoサーバを実装すると、複数 クライアントからのアクセスに対応できなかった $ python tcp_server.py waiting for connection... ...connected from: ('127.0.0.1', 52201) 2
  3. 3. #!/usr/bin/env python from socket import * from time import ctime HOST = '127.0.0.1' # localhost PORT = 4000 # choose a random port number BUFSIZ = 1024 # set buffer size to 1K ADDR = (HOST, PORT) def Server(tcpCliSock): while True: data = tcpCliSock.recv(BUFSIZ) if not data: break tcpCliSock.send('[%s] %s' % (ctime(), data)) tcpCliSock.close() def start_tcp_server(): tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) print 'waiting for connection...' while True: tcpCliSock, addr = tcpSerSock.accept() print '...connected from:', addr Server(tcpCliSock) if __name__ == '__main__': start_tcp_server() 3
  4. 4. 2. ブロッキングI/OなEchoサーバ aaa aaa Echoサーバ クライアント1 $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 15:09:10 2013] aaa aaaaa [Mon Sep 23 15:09:14 2013] aaaaa aa [Mon Sep 23 15:12:07 2013] aa クライアント2 aaa $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 15:09:38 2013] aaa マルチスレッドでEchoサーバを実装すると、複数ク ライアントからのアクセスに対応できた $ python tcp_server_threading.py waiting for connection... ...connected from: ('127.0.0.1', 52363) ...connected from: ('127.0.0.1', 52370) aaa マルチ スレッド 4
  5. 5. #!/usr/bin/env python import threading from socket import * from time import ctime HOST = '127.0.0.1' # localhost PORT = 4000 # choose a random port number BUFSIZ = 1024 # set buffer size to 1K ADDR = (HOST, PORT) def Server(tcpCliSock): while True: data = tcpCliSock.recv(BUFSIZ) if not data: break tcpCliSock.send('[%s] %s' % (ctime(), data)) tcpCliSock.close() def start_tcp_server(): tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) print 'waiting for connection...' while True: tcpCliSock, addr = tcpSerSock.accept() print '...connected from:', addr threading.Thread(target=Server, args=(tcpCliSock,)).start() if __name__ == '__main__': start_tcp_server() 5
  6. 6. ユーザからの処理をブロッキングI/Oで処理する場合には、 マルチスレッドでアプリケーションを作成する必要があ り、スレッド間の同期の問題などが発生するので、細心の 注意が必要になる。 ノンブロッキングI/Oで処理する場合には、1つのスレッド で複数のコネクションを扱うことが可能となる。 なお、1つのスレッドで複数のユーザからの処理を制御す るスケジューリング処理は、OSではなくアプリケーショ ン側で実施しなければならない。 ブロッキングI/O ノンブロッキングI/O 今回、Eventletに着目してみた。 6
  7. 7. eventletとは Eventlet is built around the concept of green threads (i.e. coroutines, we use the terms interchangeably) that are launched to do network-related work. Green threads differ from normal threads in two main ways: • Green threads are so cheap they are nearly free. You do not have to conserve green threads like you would normal threads. In general, there will be at least one green thread per network connection. • Green threads cooperatively yield to each other instead of preemptively being scheduled. The major advantage from this behavior is that shared data structures don’t need locks, because only if a yield is explicitly called can another green thread have access to the data structure. It is also possible to inspect primitives such as queues to see if they have any pending data. http://eventlet.net/doc/basic_usage.html 7
  8. 8. 3. ノンブロッキングI/OなEchoサーバ aaa aaa Echoサーバ クライアント1 $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 15:55:56 2013] aaa aaaaa [Mon Sep 23 15:55:59 2013] aaaaa aa [Mon Sep 23 15:56:13 2013] aa クライアント2 aaa $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 15:56:10 2013] aaa シングル スレッド まず、Eventletのmonkey_patchで、対応してみた。 $ python tcp_server_threading_monkeypatch.py waiting for connection... ...connected from: ('127.0.0.1', 52584) ...connected from: ('127.0.0.1', 52585) aaa 8
  9. 9. #!/usr/bin/env python import eventlet; eventlet.monkey_patch() import threading from socket import * from time import ctime HOST = '127.0.0.1' # localhost PORT = 4000 # choose a random port number BUFSIZ = 1024 # set buffer size to 1K ADDR = (HOST, PORT) def Server(tcpCliSock): while True: data = tcpCliSock.recv(BUFSIZ) if not data: break tcpCliSock.send('[%s] %s' % (ctime(), data)) tcpCliSock.close() def start_tcp_server(): tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) print 'waiting for connection...' while True: tcpCliSock, addr = tcpSerSock.accept() print '...connected from:', addr threading.Thread(target=Server, args=(tcpCliSock,)).start() if __name__ == '__main__': start_tcp_server() ブロッキングI/Oでのマルチスレッド プログラミングと、ほぼ同一のまま、 ノンブロッキングI/O処理が実現可能 9
  10. 10. 4. ノンブロッキングI/OなEchoサーバ aaa aaa Echoサーバ クライアント1 $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 15:45:12 2013] aaa aaaaa [Mon Sep 23 15:45:15 2013] aaaaa aa [Mon Sep 23 15:45:38 2013] aa クライアント2 aaa $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 15:45:26 2013] aaa シングル スレッド $ python tcp_server_eventlet.py waiting for connection... ...connected from: ('127.0.0.1', 52524) ...connected from: ('127.0.0.1', 52525) aaa 次に、Eventletの各種APIを活用して、対応してみた。 10
  11. 11. #!/usr/bin/env python import eventlet from time import ctime HOST = '127.0.0.1' # localhost PORT = 4000 # choose a random port number BUFSIZ = 1024 # set buffer size to 1K ADDR = (HOST, PORT) def handler(tcpCliSock, ADDR): print '...connected from:', ADDR while True: data = tcpCliSock.recv(BUFSIZ) if not data: break tcpCliSock.send('[%s] %s' % (ctime(), data)) tcpCliSock.close() def start_tcp_server(): print 'waiting for connection...' server = eventlet.listen(ADDR) eventlet.serve(server, handler) if __name__ == '__main__': start_tcp_server() 11
  12. 12. 5. ノンブロッキングI/OなEchoサーバ aaa aaa Echoサーバ クライアント1 $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 16:14:38 2013] aaa aaaaa [Mon Sep 23 16:14:40 2013] aaaaa aa [Mon Sep 23 16:14:57 2013] aa クライアント2 aaa $ telnet 127.0.0.1 4000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. aaa [Mon Sep 23 16:14:53 2013] aaa シングル スレッド $ python tcp_server_eventlet_queue.py waiting for connection... ...connected from: ('127.0.0.1', 52622) ...connected from: ('127.0.0.1', 52623) aaa 最後に、Eventletのqueue活用して、対応してみた。 queue queue put get put get 12
  13. 13. #!/usr/bin/env python import eventlet from time import ctime HOST = '127.0.0.1' # localhost PORT = 4000 # choose a random port number BUFSIZ = 1024 # set buffer size to 1K ADDR = (HOST, PORT) def _recv_loop(queue, sock): while True: data = sock.recv(4096) if len(data) == 0: break queue.put(data) def _send_loop(queue, sock): while True: data = queue.get() if not data: break sock.sendall('[%s] %s' % (ctime(), data)) def serve(queue, sock): eventlet.spawn(_send_loop, queue, sock) _recv_loop(queue, sock) def start_tcp_server(): print 'waiting for connection...' server = eventlet.listen(ADDR) while True: sock, addr = server.accept() print '...connected from:', addr tx_queue = eventlet.queue.Queue() eventlet.spawn(serve, tx_queue, sock) if __name__ == '__main__': start_tcp_server() 13
  14. 14. ノンブロッリングI/OをEventlet活用して Echoサーバを実装してみた。 感覚的には、シングルスレッド的に実装で きるところが、Good !! あと、ネイティブスレッドと異なり、 グリーンスレッドは、ユーザ空間で動作する ので、グリーンスレッド切り替え処理をプロ グラマ側で制御できるのが利点らしい。 まだまだ、API活用事例が少ないため、 英語ドキュメントの読解力が必要みたい。 14

×