webpy 请求处理分析

介绍

通过一个todolist的例子来分析请求处理的大概的流程。

过程

app.run()服务开始运行

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
class application:
def run(self, middleware):
return wsgi.runwsgi(self.wsgifunc(middleware))

#这里的传递fun是wsgi中的app
def runwsgi(func):
#判断fcgi、scgi、simple server,使用simple server
httpserver.runsimple(func, server_addr)

def runsimple(func, server_address=(“0.0.0.0”, 8080)):
server = WSGIServer(server_address, func)
server.start()

#这个WSGIServer是一个方法,不是类
def WSGIServer(server_address, wsgi_app):
import wsgiserver
server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name=“localhost”)
return server

class CherryPyWSGIServer(HTTPServer):
def init(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):

#初始化线程池
self.requests = ThreadPool(self, min=numthreads or 1, max=max)
self.wsgi_app = wsgi_app
#用于调用wsgi处理请求并回写给客户端
self.gateway = wsgi_gateways[self.wsgi_version]

class HTTPServer(object):
#start实在runsimple方法中调用了
#获取socket信息,绑定监听地址和端口
#启动self.requests线程
#获取来自可断断的请求
def start(self):
self.requests.start()
self.ready = True
while self.ready:
self.tick()

#接受客户端的请求,封装为ConnectionClass,放到线程池的队列中
def tick(self):
“””Accept a new connection and put it on the Queue.”””
try:
s, addr = self.socket.accept()

conn = self.ConnectionClass(self, s, makefile)
self.requests.put(conn)

class ThreadPool(object):
def init(self, server, min=10, max=-1):
self.server = server
self.min = min
self.max = max
self._threads = []
self._queue = Queue.Queue()
self.get = self._queue.get

#创建固定数量的线程,并启动
def start(self):
“””Start the pool of threads.”””
for i in range(self.min):
self._threads.append(WorkerThread(self.server))
for worker in self._threads:
worker.setName(“CP Server “ + worker.getName())
worker.start()
def put(self, obj):
self._queue.put(obj)
if obj is _SHUTDOWNREQUEST:
return

class WorkerThread(threading.Thread):
def run(self):
while True:
conn = self.server.requests.get() #self.get = self._queue.get
conn.communicate()

class HTTPConnection(object):
def communicate(self):
while True:
#验证
req = self.RequestHandlerClass(self.server, self)
req.parse_request()
req.respond()

class HTTPRequest(object):
def respond(self):
self.server.gateway(self).respond()

class WSGIGateway_10(WSGIGateway):
def init(self, req):
self.req = req
def respond(self):
# 这里的wsgi_app就是CherryPyWSGIServer的init中传递的wsgi_app
response = self.req.server.wsgi_app(self.env, self.start_response)
for chunk in response:
if chunk:
if isinstance(chunk, unicode):
chunk = chunk.encode(‘ISO-8859-1’)
self.write(chunk)


class application:
def wsgifunc(self, *middleware):
def wsgi(env, start_resp):
self.load(env)
result = web.safestr(iter(result))
start_resp(status, headers)
return itertools.chain(result, cleanup())
return wsgi

一一对应WSGI中的角色,豁然开朗。

大概总结wsgi规则:
application要是一个可调用的对象,接受两个参数,返回一个可迭代的值
server调用application,传递给application两个参数,迭代application的返回值,传递给client

下面具体看application的实现

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
class application:
def init(self, mapping=(), fvars={}, autoreload=None):
if autoreload is None:
autoreload = web.config.get(‘debug’, False)
self.init_mapping(mapping)
self.fvars = fvars
self.processors = []
self.add_processor(loadhook(self._load))
self.add_processor(unloadhook(self._unload))
def wsgifunc(self, *middleware):
“””Returns a WSGI-compatible function for this application.”””
def wsgi(env, start_resp):
# clear threadlocal to avoid inteference of previous requests
self._cleanup()
self.load(env)
try:
# allow uppercase methods only
if web.ctx.method.upper() != web.ctx.method:
raise web.nomethod()
result = self.handle_with_processors()
if is_generator(result):
result = peep(result)
else:
result = [result]
except web.HTTPError, e:
result = [e.data]
result = web.safestr(iter(result))
status, headers = web.ctx.status, web.ctx.headers
start_resp(status, headers)
def cleanup():
self._cleanup()
yield ‘’ # force this function to be a generator
return itertools.chain(result, cleanup())
for m in middleware:
wsgi = m(wsgi)
return wsgi

def init_mapping(self, mapping):
self.mapping = list(utils.group(mapping, 2))
#将元组转换为类似[[‘/‘, ‘Index’], [‘/del/(\d+)’, ‘Delete’]]

# 执行self.handle()
def handle_with_processors(self):
# return self.processors0))
def process(processors):
try:
if processors:
p, processors = processors[0], processors[1:]
return p(lambda: process(processors))
else:
return self.handle()
except web.HTTPError:
raise
except (KeyboardInterrupt, SystemExit):
raise
except:
print >> web.debug, traceback.format_exc()
raise self.internalerror()

# processors must be applied in the resvere order. (??)
return process(self.processors)

def handle(self):
fn, args = self._match(self.mapping, web.ctx.path)
return self._delegate(fn, self.fvars, args)
# 加入请求路径是 /
# _match 获得 Index 和 args
# _delegate 执行 globals()中的Index的实例中meth对应方法的值返回

参考

https://www.python.org/dev/peps/pep-3333/
http://webpy.org/src/todo-list/0.3