Python的Web框架中Tornado以异步非阻塞而闻名。本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow。
一、源码
本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞的Web框架,其中便是众多异步非阻塞Web框架内部原理。
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import re 4 import socket 5 import select 6 import time 7 8 9 class HttpResponse(object): 10 """ 11 封装响应信息 12 """ 13 def __init__(self, content=''): 14 self.content = content 15 16 self.headers = {} 17 self.cookies = {} 18 19 def response(self): 20 return bytes(self.content, encoding='utf-8') 21 22 23 class HttpNotFound(HttpResponse): 24 """ 25 404时的错误提示 26 """ 27 def __init__(self): 28 super(HttpNotFound, self).__init__('404 Not Found') 29 30 31 class HttpRequest(object): 32 """ 33 用户封装用户请求信息 34 """ 35 def __init__(self, conn): 36 self.conn = conn 37 38 self.header_bytes = bytes() 39 self.header_dict = {} 40 self.body_bytes = bytes() 41 42 self.method = "" 43 self.url = "" 44 self.protocol = "" 45 46 self.initialize() 47 self.initialize_headers() 48 49 def initialize(self): 50 51 header_flag = False 52 while True: 53 try: 54 received = self.conn.recv(8096) 55 except Exception as e: 56 received = None 57 if not received: 58 break 59 if header_flag: 60 self.body_bytes += received 61 continue 62 temp = received.split(b' ', 1) 63 if len(temp) == 1: 64 self.header_bytes += temp 65 else: 66 h, b = temp 67 self.header_bytes += h 68 self.body_bytes += b 69 header_flag = True 70 71 @property 72 def header_str(self): 73 return str(self.header_bytes, encoding='utf-8') 74 75 def initialize_headers(self): 76 headers = self.header_str.split(' ') 77 first_line = headers[0].split(' ') 78 if len(first_line) == 3: 79 self.method, self.url, self.protocol = headers[0].split(' ') 80 for line in headers: 81 kv = line.split(':') 82 if len(kv) == 2: 83 k, v = kv 84 self.header_dict[k] = v 85 86 87 class Future(object): 88 """ 89 异步非阻塞模式时封装回调函数以及是否准备就绪 90 """ 91 def __init__(self, callback): 92 self.callback = callback 93 self._ready = False 94 self.value = None 95 96 def set_result(self, value=None): 97 self.value = value 98 self._ready = True 99 100 @property 101 def ready(self): 102 return self._ready 103 104 105 class TimeoutFuture(Future): 106 """ 107 异步非阻塞超时 108 """ 109 def __init__(self, timeout): 110 super(TimeoutFuture, self).__init__(callback=None) 111 self.timeout = timeout 112 self.start_time = time.time() 113 114 @property 115 def ready(self): 116 current_time = time.time() 117 if current_time > self.start_time + self.timeout: 118 self._ready = True 119 return self._ready 120 121 122 class Snow(object): 123 """ 124 微型Web框架类 125 """ 126 def __init__(self, routes): 127 self.routes = routes 128 self.inputs = set() 129 self.request = None 130 self.async_request_handler = {} 131 132 def run(self, host='localhost', port=9999): 133 """ 134 事件循环 135 :param host: 136 :param port: 137 :return: 138 """ 139 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 140 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 141 sock.bind((host, port,)) 142 sock.setblocking(False) 143 sock.listen(128) 144 sock.setblocking(0) 145 self.inputs.add(sock) 146 try: 147 while True: 148 readable_list, writeable_list, error_list = select.select(self.inputs, [], self.inputs,0.005) 149 for conn in readable_list: 150 if sock == conn: 151 client, address = conn.accept() 152 client.setblocking(False) 153 self.inputs.add(client) 154 else: 155 gen = self.process(conn) 156 if isinstance(gen, HttpResponse): 157 conn.sendall(gen.response()) 158 self.inputs.remove(conn) 159 conn.close() 160 else: 161 yielded = next(gen) 162 self.async_request_handler[conn] = yielded 163 self.polling_callback() 164 165 except Exception as e: 166 pass 167 finally: 168 sock.close() 169 170 def polling_callback(self): 171 """ 172 遍历触发异步非阻塞的回调函数 173 :return: 174 """ 175 for conn in list(self.async_request_handler.keys()): 176 yielded = self.async_request_handler[conn] 177 if not yielded.ready: 178 continue 179 if yielded.callback: 180 ret = yielded.callback(self.request, yielded) 181 conn.sendall(ret.response()) 182 self.inputs.remove(conn) 183 del self.async_request_handler[conn] 184 conn.close() 185 186 def process(self, conn): 187 """ 188 处理路由系统以及执行函数 189 :param conn: 190 :return: 191 """ 192 self.request = HttpRequest(conn) 193 func = None 194 for route in self.routes: 195 if re.match(route[0], self.request.url): 196 func = route[1] 197 break 198 if not func: 199 return HttpNotFound() 200 else: 201 return func(self.request)
二、使用
1. 基本使用
1 from snow import Snow 2 from snow import HttpResponse 3 4 5 def index(request): 6 return HttpResponse('OK') 7 8 9 routes = [ 10 (r'/index/', index), 11 ] 12 13 app = Snow(routes) 14 app.run(port=8012)
2.异步非阻塞:超时
1 from snow import Snow 2 from snow import HttpResponse 3 from snow import TimeoutFuture 4 5 request_list = [] 6 7 8 def async(request): 9 obj = TimeoutFuture(5) 10 yield obj 11 12 13 def home(request): 14 return HttpResponse('home') 15 16 17 routes = [ 18 (r'/home/', home), 19 (r'/async/', async), 20 ] 21 22 app = Snow(routes) 23 app.run(port=8012)
3.异步非阻塞:等待
基于等待模式可以完成自定制操作
1 from snow import Snow 2 from snow import HttpResponse 3 from snow import Future 4 5 request_list = [] 6 7 8 def callback(request, future): 9 return HttpResponse(future.value) 10 11 12 def req(request): 13 obj = Future(callback=callback) 14 request_list.append(obj) 15 yield obj 16 17 18 def stop(request): 19 obj = request_list[0] 20 del request_list[0] 21 obj.set_result('done') 22 return HttpResponse('stop') 23 24 25 routes = [ 26 (r'/req/', req), 27 (r'/stop/', stop), 28 ] 29 30 app = Snow(routes) 31 app.run(port=8012)