自带的httpserver¶
gunicorn自带了一个python wsgi http server,是为了默认的syncworker而设计的。
class SyncWorker(base.Worker):
def run(self):
# self.socket appears to lose its blocking status after
# we fork in the arbiter. Reset it here.
self.socket.setblocking(0)
while self.alive:
self.notify()
# Accept a connection. If we get an error telling us
# that no connection is waiting we fall down to the
# select which is where we'll wait for a bit for new
# workers to come give us some love.
try:
client, addr = self.socket.accept()
client.setblocking(1)
util.close_on_exec(client)
self.handle(client, addr)
# Keep processing clients until no one is waiting. This
# prevents the need to select() for every client that we
# process.
continue
except socket.error, e:
if e[0] not in (errno.EAGAIN, errno.ECONNABORTED):
raise
终于在这里看到了久违的self.socket.accept(),接收一个访问连接,由self.handle(client, addr)来处理客户端来的请求命令。
class SyncWorker(base.Worker):
#......
def handle(self, client, addr):
try:
parser = http.RequestParser(client)
req = parser.next()
self.handle_request(req, client, addr)
except StopIteration, e:
self.log.debug("Closing connection. %s", e)
except socket.error, e:
if e[0] != errno.EPIPE:
self.log.exception("Error processing request.")
else:
self.log.debug("Ignoring EPIPE")
except Exception, e:
self.handle_error(client, e)
finally:
util.close(client)
从http.RequestParser(client)进入http模块中,在self.handle_request(req, client, addr)中调用wsgi模块
class SyncWorker(base.Worker):
#......
def handle_request(self, req, client, addr):
environ = {}
try:
self.cfg.pre_request(self, req)
request_start = datetime.now()
resp, environ = wsgi.create(req, client, addr,
self.address, self.cfg)
# Force the connection closed until someone shows
# a buffering proxy that supports Keep-Alive to
# the backend.
resp.force_close()
self.nr += 1
if self.nr >= self.max_requests:
self.log.info("Autorestarting worker after current request.")
self.alive = False
respiter = self.wsgi(environ, resp.start_response)
try:
if isinstance(respiter, environ['wsgi.file_wrapper']):
resp.write_file(respiter)
else:
for item in respiter:
resp.write(item)
resp.close()
request_time = datetime.now() - request_start
self.log.access(resp, environ, request_time)
finally:
if hasattr(respiter, "close"):
respiter.close()
except socket.error:
raise
except Exception, e:
# Only send back traceback in HTTP in debug mode.
self.handle_error(client, e)
return
finally:
try:
self.cfg.post_request(self, req, environ)
except:
pass
其中涉及到wsgi模块的有:
resp, environ = wsgi.create(req, client, addr,
self.address, self.cfg)
获得最终的响应结果:
respiter = self.wsgi(environ, resp.start_response)
这里的self.wsgi的踪迹在哪里呢?
- 由base.Worker中的方法init_process()中调用
self.init_signals()
self.wsgi = self.app.wsgi()
# Enter main run loop
self.booted = True
self.run()
- 在app,也就是wsgiapp或djangoapp中WSGIApplication -> Application中,有
def wsgi(self):
if self.callable is None:
self.callable = self.load()
return self.callable
- 直接由self.load()执行,回转到WSGIApplication中的load()
def load(self):
return util.import_app(self.app_uri)
- 调用util模块中的import_app(self.app_uri)
def import_app(module):
parts = module.split(":", 1)
if len(parts) == 1:
module, obj = module, "application"
else:
module, obj = parts[0], parts[1]
try:
__import__(module)
except ImportError:
if module.endswith(".py") and os.path.exists(module):
raise ImportError("Failed to find application, did "
"you mean '%s:%s'?" % (module.rsplit(".",1)[0], obj))
else:
raise
mod = sys.modules[module]
app = eval(obj, mod.__dict__)
if app is None:
raise ImportError("Failed to find application object: %r" % obj)
if not callable(app):
raise TypeError("Application object must be callable.")
return app
这才是最终wsgi应用程序的发起。还记得我们在快速入门中编写的简单wsgi程序么?
那么我们就在这里加上一句print,看看是不是:
print u'waiting for you ...(%s, %s)' % (module, obj)
再执行命令:
$ gunicorn --workers=2 myapp:app
结果如下:
(mypy)hzg@xubuntu:~/sample$ gunicorn --workers=2 myapp:app
2012-02-16 08:45:09 [2007] [INFO] Starting gunicorn 0.13.4
2012-02-16 08:45:09 [2007] [INFO] Listening at: http://127.0.0.1:8000 (2007)
2012-02-16 08:45:09 [2007] [INFO] Using worker: sync
2012-02-16 08:45:09 [2010] [INFO] Booting worker with pid: 2010
waiting for you ...(myapp, app)
2012-02-16 08:45:09 [2011] [INFO] Booting worker with pid: 2011
waiting for you ...(myapp, app)