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. #!/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. 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. #!/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
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. 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. #!/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. 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. #!/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. 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. #!/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