自定义异步非阻塞web框架

Python的Web框架中Tornado以异步非阻塞而闻名,本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞的Web框架,其中便是众多异步非阻塞Web框架内部原理。

图示:

 

 

上面的是异步IO模块:作为客户端发送请求给服务端,实现同时发多个请求的的功能,select监听socket是否有变化,返回对应请求响应

下面的是异步非阻塞web框架:服务端同时处理客户端发来的多个请求

举例:

1.time.sleep()设置阻塞

 1 import tornado.ioloop
 2 import tornado.web
 3 
 4 
 5 class MainHandler(tornado.web.RequestHandler):
 6     def get(self):
 7         import time
 8         time.sleep(10)
 9         self.write("Hello, world")
10 
11 class IndexHandler(tornado.web.RequestHandler):
12     def get(self):
13         self.write("index, world")
14 
15 application = tornado.web.Application([
16     (r"/main", MainHandler),
17     (r"/index", IndexHandler),
18 ])
19 
20 if __name__ == "__main__":
21     application.listen(8888)
22     tornado.ioloop.IOLoop.instance().start()
23 
24 #问题:先访问/main会等待10s,然后执行/index,这时候/index要等到/main完成后才能显示
例子1

2.增加future,解决IO阻塞问题

  1 class Future(object):
  2     """Placeholder for an asynchronous result.
  3 
  4     A ``Future`` encapsulates the result of an asynchronous
  5     operation.  In synchronous applications ``Futures`` are used
  6     to wait for the result from a thread or process pool; in
  7     Tornado they are normally used with `.IOLoop.add_future` or by
  8     yielding them in a `.gen.coroutine`.
  9 
 10     `tornado.concurrent.Future` is similar to
 11     `concurrent.futures.Future`, but not thread-safe (and therefore
 12     faster for use with single-threaded event loops).
 13 
 14     In addition to ``exception`` and ``set_exception``, methods ``exc_info``
 15     and ``set_exc_info`` are supported to capture tracebacks in Python 2.
 16     The traceback is automatically available in Python 3, but in the
 17     Python 2 futures backport this information is discarded.
 18     This functionality was previously available in a separate class
 19     ``TracebackFuture``, which is now a deprecated alias for this class.
 20 
 21     .. versionchanged:: 4.0
 22        `tornado.concurrent.Future` is always a thread-unsafe ``Future``
 23        with support for the ``exc_info`` methods.  Previously it would
 24        be an alias for the thread-safe `concurrent.futures.Future`
 25        if that package was available and fall back to the thread-unsafe
 26        implementation if it was not.
 27 
 28     .. versionchanged:: 4.1
 29        If a `.Future` contains an error but that error is never observed
 30        (by calling ``result()``, ``exception()``, or ``exc_info()``),
 31        a stack trace will be logged when the `.Future` is garbage collected.
 32        This normally indicates an error in the application, but in cases
 33        where it results in undesired logging it may be necessary to
 34        suppress the logging by ensuring that the exception is observed:
 35        ``f.add_done_callback(lambda f: f.exception())``.
 36     """
 37     def __init__(self):
 38         self._done = False
 39         self._result = None
 40         self._exc_info = None
 41 
 42         self._log_traceback = False   # Used for Python >= 3.4
 43         self._tb_logger = None        # Used for Python <= 3.3
 44 
 45         self._callbacks = []
 46 
 47     # Implement the Python 3.5 Awaitable protocol if possible
 48     # (we can't use return and yield together until py33).
 49     if sys.version_info >= (3, 3):
 50         exec(textwrap.dedent("""
 51         def __await__(self):
 52             return (yield self)
 53         """))
 54     else:
 55         # Py2-compatible version for use with cython.
 56         def __await__(self):
 57             result = yield self
 58             # StopIteration doesn't take args before py33,
 59             # but Cython recognizes the args tuple.
 60             e = StopIteration()
 61             e.args = (result,)
 62             raise e
 63 
 64     def cancel(self):
 65         """Cancel the operation, if possible.
 66 
 67         Tornado ``Futures`` do not support cancellation, so this method always
 68         returns False.
 69         """
 70         return False
 71 
 72     def cancelled(self):
 73         """Returns True if the operation has been cancelled.
 74 
 75         Tornado ``Futures`` do not support cancellation, so this method
 76         always returns False.
 77         """
 78         return False
 79 
 80     def running(self):
 81         """Returns True if this operation is currently running."""
 82         return not self._done
 83 
 84     def done(self):
 85         """Returns True if the future has finished running."""
 86         return self._done
 87 
 88     def _clear_tb_log(self):
 89         self._log_traceback = False
 90         if self._tb_logger is not None:
 91             self._tb_logger.clear()
 92             self._tb_logger = None
 93 
 94     def result(self, timeout=None):
 95         """If the operation succeeded, return its result.  If it failed,
 96         re-raise its exception.
 97 
 98         This method takes a ``timeout`` argument for compatibility with
 99         `concurrent.futures.Future` but it is an error to call it
100         before the `Future` is done, so the ``timeout`` is never used.
101         """
102         self._clear_tb_log()
103         if self._result is not None:
104             return self._result
105         if self._exc_info is not None:
106             try:
107                 raise_exc_info(self._exc_info)
108             finally:
109                 self = None
110         self._check_done()
111         return self._result
112 
113     def exception(self, timeout=None):
114         """If the operation raised an exception, return the `Exception`
115         object.  Otherwise returns None.
116 
117         This method takes a ``timeout`` argument for compatibility with
118         `concurrent.futures.Future` but it is an error to call it
119         before the `Future` is done, so the ``timeout`` is never used.
120         """
121         self._clear_tb_log()
122         if self._exc_info is not None:
123             return self._exc_info[1]
124         else:
125             self._check_done()
126             return None
127 
128     def add_done_callback(self, fn):
129         """Attaches the given callback to the `Future`.
130 
131         It will be invoked with the `Future` as its argument when the Future
132         has finished running and its result is available.  In Tornado
133         consider using `.IOLoop.add_future` instead of calling
134         `add_done_callback` directly.
135         """
136         if self._done:
137             fn(self)
138         else:
139             self._callbacks.append(fn)
140 
141     def set_result(self, result):
142         """Sets the result of a ``Future``.
143 
144         It is undefined to call any of the ``set`` methods more than once
145         on the same object.
146         """
147         self._result = result
148         self._set_done()
149 
150     def set_exception(self, exception):
151         """Sets the exception of a ``Future.``"""
152         self.set_exc_info(
153             (exception.__class__,
154              exception,
155              getattr(exception, '__traceback__', None)))
156 
157     def exc_info(self):
158         """Returns a tuple in the same format as `sys.exc_info` or None.
159 
160         .. versionadded:: 4.0
161         """
162         self._clear_tb_log()
163         return self._exc_info
164 
165     def set_exc_info(self, exc_info):
166         """Sets the exception information of a ``Future.``
167 
168         Preserves tracebacks on Python 2.
169 
170         .. versionadded:: 4.0
171         """
172         self._exc_info = exc_info
173         self._log_traceback = True
174         if not _GC_CYCLE_FINALIZERS:
175             self._tb_logger = _TracebackLogger(exc_info)
176 
177         try:
178             self._set_done()
179         finally:
180             # Activate the logger after all callbacks have had a
181             # chance to call result() or exception().
182             if self._log_traceback and self._tb_logger is not None:
183                 self._tb_logger.activate()
184         self._exc_info = exc_info
185 
186     def _check_done(self):
187         if not self._done:
188             raise Exception("DummyFuture does not support blocking for results")
189 
190     def _set_done(self):
191         self._done = True
192         for cb in self._callbacks:
193             try:
194                 cb(self)
195             except Exception:
196                 app_log.exception('Exception in callback %r for %r',
197                                   cb, self)
198         self._callbacks = None
199 
200     # On Python 3.3 or older, objects with a destructor part of a reference
201     # cycle are never destroyed. It's no longer the case on Python 3.4 thanks to
202     # the PEP 442.
203     if _GC_CYCLE_FINALIZERS:
204         def __del__(self, is_finalizing=is_finalizing):
205             if is_finalizing() or not self._log_traceback:
206                 # set_exception() was not called, or result() or exception()
207                 # has consumed the exception
208                 return
209 
210             tb = traceback.format_exception(*self._exc_info)
211 
212             app_log.error('Future %r exception was never retrieved: %s',
213                           self, ''.join(tb).rstrip())
Future类源码
1 #future = Future()
2 #作用:
3 #1.挂起当前请求,线程可以处理其他请求
4 #2.future设置值,当前挂起的请求返回
 1 import tornado.ioloop
 2 import tornado.web
 3 from tornado import gen
 4 from tornado.concurrent import Future
 5 import time
 6 #解决一个IO堵塞时,另外一个请求只能等待上一个请求完成后才能继续
 7 
 8 class MainHandler(tornado.web.RequestHandler):
 9     @gen.coroutine
10     def get(self):
11         future = Future()
12         # 特殊的形式等待5s,5s后执行doing方法
13         tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5,self.doing)
14         # self.write("Hello, world")
15         yield future
16 
17     def doing(self,*args,**kwargs):
18         self.write('Main End')
19         self.finish()
20 
21 class IndexHandler(tornado.web.RequestHandler):
22     def get(self):
23         self.write("index, world")
24 
25 application = tornado.web.Application([
26     (r"/main", MainHandler),
27     (r"/index", IndexHandler),
28 ])
29 
30 if __name__ == "__main__":
31     application.listen(8888)
32     tornado.ioloop.IOLoop.instance().start()
33 
34 #若开始先请求/main然后请求/index,由于MainHandler有future会先挂起当前请求,线程可以处理其他请求
35 #因此看到的是后面开始请求的/index直接处理完请求返回,而/main还需要等待5s才返回
例子2

 3.内部再进行请求IO阻塞

 1 import tornado.ioloop
 2 import tornado.web
 3 from tornado.concurrent import Future
 4 from tornado import gen
 5 
 6 class MainHandler(tornado.web.RequestHandler):
 7     def get(self):
 8         # 需要等待请求执行完成,才能处理下一个请求
 9         import requests
10         requests.get('http://www.google.com')
11         self.write('Main')
12 
13 class IndexHandler(tornado.web.RequestHandler):
14     def get(self):
15         self.write("index, world")
16 
17 application = tornado.web.Application([
18     (r"/main", MainHandler),
19     (r"/index", IndexHandler),
20 ])
21 
22 if __name__ == "__main__":
23     application.listen(8888)
24     tornado.ioloop.IOLoop.instance().start()
例子3

4.AsyncHTTPClient异步模块,解决IO阻塞问题

 1 import tornado.ioloop
 2 import tornado.web
 3 from tornado.concurrent import Future
 4 from tornado import gen
 5 
 6 class MainHandler(tornado.web.RequestHandler):
 7     @gen.coroutine
 8     def get(self):
 9         # 需要等待请求执行完成,才能处理下一个请求
10         # import requests
11         # requests.get('http://www.google.com')
12         # self.write('xxxx')
13 
14         print('进入')
15         from tornado import httpclient
16         http = httpclient.AsyncHTTPClient()
17         yield http.fetch("http://www.google.com", self.done)
18 
19     def done(self,*args,**kwargs):
20         print('完事')
21         self.write('Main')
22         self.finish('666')
23 
24 class IndexHandler(tornado.web.RequestHandler):
25     def get(self):
26         self.write("index, world")
27 
28 application = tornado.web.Application([
29     (r"/main", MainHandler),
30     (r"/index", IndexHandler),
31 ])
32 
33 if __name__ == "__main__":
34     application.listen(8888)
35     tornado.ioloop.IOLoop.instance().start()
例子4

5.future实现异步非阻塞(set_result)

 1 import tornado.ioloop
 2 import tornado.web
 3 from tornado.concurrent import Future
 4 from tornado import gen
 5 
 6 future = None
 7 class MainHandler(tornado.web.RequestHandler):
 8     @gen.coroutine
 9     def get(self):
10         global future
11         future = Future()
12         future.add_done_callback(self.doing) #未给future设置值,挂起当前请求
13         yield future
14 
15     def doing(self,*args,**kwargs):
16         self.write('Main')
17         self.finish()
18 
19 class IndexHandler(tornado.web.RequestHandler):
20     def get(self):
21         global future
22         future.set_result(None)  #给future设置值,当前挂起的请求返回
23         self.write("index, world")
24 
25 application = tornado.web.Application([
26     (r"/main", MainHandler),
27     (r"/index", IndexHandler),
28 ])
29 
30 if __name__ == "__main__":
31     application.listen(8888)
32     tornado.ioloop.IOLoop.instance().start()
33     
34 #情况就是:先访问/main,暂时挂起,然后访问/index,对应的类get方法给future设置值,因此两个请求基本同时完成
例子5

 自定义web框架:

  1 import socket
  2 import select
  3 import time
  4 
  5 class HttpRequest(object):
  6     """
  7     用户封装用户请求信息
  8     """
  9     def __init__(self, content):
 10         """
 11 
 12         :param content: 用户发送的请求数据,请求头和请求体
 13         """
 14         self.content = content
 15 
 16         self.header_bytes = bytes()
 17         self.body_bytes = bytes()
 18 
 19         self.header_dict = {}
 20 
 21         self.method = ""
 22         self.url = ""
 23         self.protocol = ""
 24 
 25         self.initialize()
 26         self.initialize_headers()
 27 
 28     #分割请求头和请求体
 29     def initialize(self):
 30         temp = self.content.split(b'\r\n\r\n', 1)
 31         print(temp)
 32         if len(temp) == 1:
 33             self.header_bytes += temp[0]
 34         else:
 35             h, b = temp[0],temp[1]
 36             print(h,b)
 37             self.header_bytes += h
 38             self.body_bytes += b
 39     @property
 40     def header_str(self):
 41         return str(self.header_bytes, encoding='utf-8')
 42 
 43     def initialize_headers(self):
 44         headers = self.header_str.split('\r\n')
 45         first_line = headers[0].split(' ')
 46         if len(first_line) == 3:
 47             self.method, self.url, self.protocol = headers[0].split(' ')
 48             for line in headers:
 49                 kv = line.split(':')
 50                 if len(kv) == 2:
 51                     k, v = kv
 52                     self.header_dict[k] = v
 53 
 54 def index(request):
 55     return 'index'
 56 
 57 def main(request):
 58     time.sleep(10)
 59     return 'main'
 60 
 61 
 62 routers = [
 63     ('/index',index),
 64     ('/main',main)
 65 
 66 ]
 67 
 68 def run():
 69     sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 70     sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 71     sock.bind(('127.0.0.1',8888))
 72     sock.setblocking(False) #设置服务端非阻塞
 73     sock.listen(10)
 74 
 75     inputs = []
 76     inputs.append(sock)
 77 
 78     while True:
 79         rlist,wlist,elist = select.select(inputs,[],[],0.05)
 80         for r in rlist:
 81             if r == sock:
 82                 """新请求到来"""
 83                 conn,addr = sock.accept() #返回客户端的conn以及地址
 84                 conn.setblocking(False) #设置客户端非阻塞
 85                 inputs.append(conn) #监听客户端和服务端的socket变化
 86             else:
 87                 """客户端发来数据"""
 88                 data = b""
 89                 while True:
 90                     try:
 91                         chunk = r.recv(1024)
 92                         data = data + chunk
 93                     except Exception as e:
 94                         chunk = None
 95                     if not chunk:
 96                         break
 97 
 98                 # data进行处理,请求头和请求体
 99                 # 1.请求头获取url
100                 # 2.去路由中匹配,获取指定的函数
101                 # 3.执行函数,获取返回值
102                 # 4.将返回值 r.sendall(b'dfafsfsg)
103                 
104                 request = HttpRequest(data)
105                 # print(request.url)
106                 # print(request.method)
107                 # print(request.header_dict)
108                 # print(request.body_bytes)
109         
110                 ### url匹配路由 ###
111                 import re
112                 flag = False #是否匹配成功
113                 func = None  #url匹配对应的函数
114                 for route in routers:
115                     if re.match(route[0],request.url):
116                         flag = True
117                         func = route[1]
118                         break
119                 if flag:
120                     result = func(request)
121                     r.sendall(bytes(result,encoding='utf-8'))
122                 else:
123                     r.sendall(b'404')
124 
125 
126                 #由于模拟http短链接,响应后断开
127                 inputs.remove(r)
128                 r.close() #关闭
129 
130 if __name__ == '__main__':
131     run()
自定义同步web框架
  1 import socket
  2 import select
  3 import time
  4 
  5 
  6 class HttpRequest(object):
  7     """
  8     用户封装用户请求信息
  9     """
 10 
 11     def __init__(self, content):
 12         """
 13 
 14         :param content: 用户发送的请求数据,请求头和请求体
 15         """
 16         self.content = content
 17 
 18         self.header_bytes = bytes()
 19         self.body_bytes = bytes()
 20 
 21         self.header_dict = {}
 22 
 23         self.method = ""
 24         self.url = ""
 25         self.protocol = ""
 26 
 27         self.initialize()
 28         self.initialize_headers()
 29 
 30     # 分割请求头和请求体
 31     def initialize(self):
 32         temp = self.content.split(b'\r\n\r\n', 1)
 33         print(temp)
 34         if len(temp) == 1:
 35             self.header_bytes += temp[0]
 36         else:
 37             h, b = temp[0], temp[1]
 38             print(h, b)
 39             self.header_bytes += h
 40             self.body_bytes += b
 41 
 42     @property
 43     def header_str(self):
 44         return str(self.header_bytes, encoding='utf-8')
 45 
 46     def initialize_headers(self):
 47         headers = self.header_str.split('\r\n')
 48         first_line = headers[0].split(' ')
 49         if len(first_line) == 3:
 50             self.method, self.url, self.protocol = headers[0].split(' ')
 51             for line in headers:
 52                 kv = line.split(':')
 53                 if len(kv) == 2:
 54                     k, v = kv
 55                     self.header_dict[k] = v
 56 
 57 F = None
 58 
 59 class Future(object):
 60     def __init__(self):
 61         self.result = None
 62 
 63 def main(request):
 64     global F
 65     F = Future()
 66     return F
 67 
 68 def index(request):
 69     return 'index'
 70 
 71 def stop(request):
 72     global F
 73     F.result = b'xx'
 74     return 'Stop'
 75 
 76 routers = [
 77     ('/index', index),
 78     ('/main', main),
 79     ('/stop',stop)
 80 
 81 ]
 82 
 83 def run():
 84     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 85     sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 86     sock.bind(('127.0.0.1', 8888))
 87     sock.setblocking(False)  # 设置服务端非阻塞
 88     sock.listen(10)
 89 
 90     inputs = []
 91     inputs.append(sock)
 92 
 93     async_request_dict = {}
 94 
 95     while True:
 96         rlist, wlist, elist = select.select(inputs, [], [], 0.05)
 97         for r in rlist:
 98             if r == sock:
 99                 """新请求到来"""
100                 conn, addr = sock.accept()  # 返回客户端的conn以及地址
101                 conn.setblocking(False)  # 设置客户端非阻塞
102                 inputs.append(conn)  # 监听客户端和服务端的socket变化
103             else:
104                 """客户端发来数据"""
105                 data = b""
106                 while True:
107                     try:
108                         chunk = r.recv(1024)
109                         data = data + chunk
110                     except Exception as e:
111                         chunk = None
112                     if not chunk:
113                         break
114 
115                 # data进行处理,请求头和请求体
116                 # 1.请求头获取url
117                 # 2.去路由中匹配,获取指定的函数
118                 # 3.执行函数,获取返回值
119                 # 4.将返回值 r.sendall(b'dfafsfsg)
120 
121                 request = HttpRequest(data)
122                 # print(request.url)
123                 # print(request.method)
124                 # print(request.header_dict)
125                 # print(request.body_bytes)
126 
127                 ### url匹配路由 ###
128                 import re
129                 flag = False  # 是否匹配成功
130                 func = None  # url匹配对应的函数
131                 for route in routers:
132                     if re.match(route[0], request.url):
133                         flag = True
134                         func = route[1]
135                         break
136                 if flag:
137                     result = func(request)
138                     #### 判断result的类型是否是Future ####
139                     if isinstance(result,Future):
140                         async_request_dict[r] = result #将客户端和future对象相关联
141                     else:
142                         r.sendall(bytes(result, encoding='utf-8'))
143                         # 由于模拟http短链接,响应后断开
144                         inputs.remove(r)
145                         r.close()  # 关闭
146                 else:
147                     r.sendall(b'404')
148                     # 由于模拟http短链接,响应后断开
149                     inputs.remove(r)
150                     r.close()  # 关闭
151 
152         # 客户端conn和future对象组成的,判断future是否设置值,若设置则对应挂起请求返回
153         for conn in async_request_dict.keys():
154             future = async_request_dict[conn]
155             if future.result:
156                 conn.send(future.result)
157                 conn.close()
158                 del async_request_dict[conn]
159                 inputs.remove(conn)
160 
161 if __name__ == '__main__':
162     run()
自定义异步非阻塞web框架
  1 import socket
  2 import select
  3 import time
  4 
  5 
  6 class HttpRequest(object):
  7     """
  8     用户封装用户请求信息
  9     """
 10 
 11     def __init__(self, content):
 12         """
 13 
 14         :param content: 用户发送的请求数据,请求头和请求体
 15         """
 16         self.content = content
 17 
 18         self.header_bytes = bytes()
 19         self.body_bytes = bytes()
 20 
 21         self.header_dict = {}
 22 
 23         self.method = ""
 24         self.url = ""
 25         self.protocol = ""
 26 
 27         self.initialize()
 28         self.initialize_headers()
 29 
 30     # 分割请求头和请求体
 31     def initialize(self):
 32         temp = self.content.split(b'\r\n\r\n', 1)
 33         print(temp)
 34         if len(temp) == 1:
 35             self.header_bytes += temp[0]
 36         else:
 37             h, b = temp[0], temp[1]
 38             print(h, b)
 39             self.header_bytes += h
 40             self.body_bytes += b
 41 
 42     @property
 43     def header_str(self):
 44         return str(self.header_bytes, encoding='utf-8')
 45 
 46     def initialize_headers(self):
 47         headers = self.header_str.split('\r\n')
 48         first_line = headers[0].split(' ')
 49         if len(first_line) == 3:
 50             self.method, self.url, self.protocol = headers[0].split(' ')
 51             for line in headers:
 52                 kv = line.split(':')
 53                 if len(kv) == 2:
 54                     k, v = kv
 55                     self.header_dict[k] = v
 56 
 57 F = None
 58 
 59 class Future(object):
 60     def __init__(self,timeout=0):
 61         self.result = None
 62         self.timeout = timeout
 63         self.start = time.time()
 64 
 65 def main(request):
 66     global F
 67     F = Future()
 68     F.timeout = 2
 69     return F
 70 
 71 def index(request):
 72     return 'index'
 73 
 74 # def stop(request):
 75 #     global F
 76 #     F.result = b'xx'
 77 #     return 'Stop'
 78 
 79 routers = [
 80     ('/index', index),
 81     ('/main', main),
 82     # ('/stop',stop)
 83 
 84 ]
 85 
 86 def run():
 87     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 88     sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 89     sock.bind(('127.0.0.1', 8888))
 90     sock.setblocking(False)  # 设置服务端非阻塞
 91     sock.listen(10)
 92 
 93     inputs = []
 94     inputs.append(sock)
 95 
 96     async_request_dict = {}
 97 
 98     while True:
 99         rlist, wlist, elist = select.select(inputs, [], [], 0.05)
100         for r in rlist:
101             if r == sock:
102                 """新请求到来"""
103                 conn, addr = sock.accept()  # 返回客户端的conn以及地址
104                 conn.setblocking(False)  # 设置客户端非阻塞
105                 inputs.append(conn)  # 监听客户端和服务端的socket变化
106             else:
107                 """客户端发来数据"""
108                 data = b""
109                 while True:
110                     try:
111                         chunk = r.recv(1024)
112                         data = data + chunk
113                     except Exception as e:
114                         chunk = None
115                     if not chunk:
116                         break
117 
118                 # data进行处理,请求头和请求体
119                 # 1.请求头获取url
120                 # 2.去路由中匹配,获取指定的函数
121                 # 3.执行函数,获取返回值
122                 # 4.将返回值 r.sendall(b'dfafsfsg)
123 
124                 request = HttpRequest(data)
125                 # print(request.url)
126                 # print(request.method)
127                 # print(request.header_dict)
128                 # print(request.body_bytes)
129 
130                 ### url匹配路由 ###
131                 import re
132                 flag = False  # 是否匹配成功
133                 func = None  # url匹配对应的函数
134                 for route in routers:
135                     if re.match(route[0], request.url):
136                         flag = True
137                         func = route[1]
138                         break
139                 if flag:
140                     result = func(request)
141                     #### 判断result的类型是否是Future ####
142                     if isinstance(result,Future):
143                         async_request_dict[r] = result #将客户端和future对象相关联
144                     else:
145                         r.sendall(bytes(result, encoding='utf-8'))
146                         # 由于模拟http短链接,响应后断开
147                         inputs.remove(r)
148                         r.close()  # 关闭
149                 else:
150                     r.sendall(b'404')
151                     # 由于模拟http短链接,响应后断开
152                     inputs.remove(r)
153                     r.close()  # 关闭
154 
155         # 客户端conn和future对象组成的,判断future是否设置值,若设置则对应挂起请求返回
156         for conn in async_request_dict.keys():
157             future = async_request_dict[conn]
158             start = future.start
159             timeout = future.timeout  #超时时间
160             ctime = time.time()
161             if (start + timeout) <= ctime:  #超时时间比较
162                 future.result = b'timeout'
163             if future.result:
164                 conn.send(future.result)
165                 conn.close()
166                 del async_request_dict[conn]
167                 inputs.remove(conn)
168 
169 if __name__ == '__main__':
170     run()
增加timeout功能

相关文档:https://www.cnblogs.com/wupeiqi/p/6536518.html

posted @ 2023-12-08 15:12  木屐呀  阅读(11)  评论(0编辑  收藏  举报
//增加一段JS脚本,为目录生成使用