Tornado
Tornado 是一款非阻塞可擴展的使用Python編寫的web服務器和Python Web框架, 可以使用Tornado編寫Web程序并不依賴任何web服務器直接提供高效的web服務.所以Tornado不僅僅是一個web框架而且還是一款可以用于生產環境的高效的web服務器
Torando 在Linux和FreeBSD上使用高效的異步I/O模型 epoll 和kqueue來實現高效的web服務器, 所以 tornado在Linux上和FreeBSD系列性能可以達到最高
接口
當然我們可以不僅僅把Tornado看作是一個web框架和web服務器, 我們可以利用Tornado提供的接口進行高效的網絡異步編程,
tornado.ioloop.IOLoop 提供了三個接口可以用于網絡編程:
1
2
3
4
5
|
add_handler def add_handler( self , fd, handler, events): self ._handlers[fd] = stack_context.wrap(handler) self ._impl.register(fd, events | self .ERROR) |
add_handler用于添加socket到主循環中, 接受三個參數: fd 是socket的文件描述符 handler 是處理此socket的 callback函數 * events 是此socket注冊的事件
1
2
3
4
|
update_handler def update_handler( self , fd, events): self ._impl.modify(fd, events | self .ERROR) |
update_handler用于更新住循環中已存在的socket響應事件, 接受兩個參數: fd 是socket對應的文件描述符 events 是注冊的新事件
1
2
3
4
5
6
7
8
9
|
remove_handler def remove_handler( self , fd): self ._handlers.pop(fd, None ) self ._events.pop(fd, None ) try : self ._impl.unregister(fd) except Exception: gen_log.debug( "Error deleting fd from IOLoop" , exc_info = True ) |
remove_handler用于移除主循環中已存在的socket
事件
tornado.ioloop.IOLoop同時提供了4種響應事件:
實例
根據上面的接口和事件我們就可以寫出一個簡單的 echo server
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
#!/usr/bin/env python # -*- coding:utf-8 -*- # # Author : cold # E-mail : [email protected] # Date : 13/04/15 15:08:51 # Desc : Tornado Echo Server # HOME : http://www.linuxzen.com # import Queue import socket from functools import partial from tornado.ioloop import IOLoop sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking( 0 ) # 將socket設置為非阻塞 server_address = ( "localhost" , 10000 ) sock.bind(server_address) sock.listen( 5 ) fd_map = {} # 文件描述符到socket的映射 message_queue_map = {} # socket到消息隊列的映射 fd = sock.fileno() fd_map[fd] = sock ioloop = IOLoop.instance() def handle_client(cli_addr, fd, event): s = fd_map[fd] if event & IOLoop.READ: data = s.recv( 1024 ) if data: print " received '%s' from %s" % (data, cli_addr) # 接收到消息更改事件為寫, 用于發送數據到對端 ioloop.update_handler(fd, IOLoop.WRITE) message_queue_map[s].put(data) else : print " closing %s" % cli_addr ioloop.remove_handler(fd) s.close() del message_queue_map[s] if event & IOLoop.WRITE: try : next_msg = message_queue_map[s].get_nowait() except Queue.Empty: print "%s queue empty" % cli_addr ioloop.update_handler(fd, IOLoop.READ) else : print 'sending "%s" to %s' % (next_msg, cli_addr) s.send(next_msg) if event & IOLoop.ERROR: print " exception on %s" % cli_addr ioloop.remove_handler(fd) s.close() del message_queue_map[s] def handle_server(fd, event): s = fd_map[fd] if event & IOLoop.READ: conn, cli_addr = s.accept() print " connection %s" % cli_addr[ 0 ] conn.setblocking( 0 ) conn_fd = conn.fileno() fd_map[conn_fd] = conn handle = partial(handle_client, cli_addr[ 0 ]) # 將cli_addr作為第一個參數 # 將連接和handle注冊為讀事件加入到 tornado ioloop ioloop.add_handler(conn_fd, handle, IOLoop.READ) message_queue_map[conn] = Queue.Queue() # 創建對應的消息隊列 ioloop.add_handler(fd, handle_server, IOLoop.READ) ioloop.start() |
上面代碼就建立了一個非阻塞的高效的異步的echo server