WSGI (Web Server Gateway Interface),WSGI是为Python语言定义的Web服务器和Web应用程序之间的一种通用接口。web
下面用一个例子来讲明WSGI的工做模式:django
from wsgiref.simple_server import make_server def simple_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [u"This is hello wsgi app".encode('utf8')] httpd = make_server('', 8000, simple_app) print "Serving on port 8000..." httpd.serve_forever()
例子中, simple_app函数是符合WSGI标准的一个HTTP处理函数,它有两个参数:flask
此外,simple_app函数的return语句返回的iterator对象做为HTTP响应body服务器
实际上,wsgi app只要是一个callable对象就能够了,所以不必定要是函数,一个实现了__call__方法的实例也能够,示例代码以下:网络
from wsgiref.simple_server import make_server class AppClass: def __call__(self, environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ["hello world!"] app = AppClass() httpd = make_server('', 8000, app) print "Serving on port 8000..." httpd.serve_forever()
Middleware的概念没有appllication和server那么容易理解。
假设一个符合application标准的可调用对象,它接受可调用对象做为参数,返回一个可调用对象的对象。
那么对于server来讲,它是一个符合标准的可调用对象,所以是application。
而对于application来讲,它能够调用application,所以是server。
这样的可调用对象称为middleware,middleware的概念很是接近decorator。session
middleware的例子:app
def exampleApplication(environ, start_response): if environ['superSession'].has_key('visited'): text = "You have already visited!" else: environ['superSession']['visited'] = 1 text = "This is your first visit." start_response('200 OK', [('Content-type','text/plain')]) return [text] def session(application): def app(environ, start_response): if "superSession" not in environ: import superSession environ["superSession"] = superSession.session() return application(environ, start_response) return app application = session(exampleApplication)
如上面,session函数用于判断用户访问行为。session函数将判断结果至于环境变量environ字典中。
exampleApplication经过environ字典得到用户访问行为。
咱们称session函数为middleware,它处于server与application之间,对server传来的请求作相应的处理;它对于Server和application是透明的。
middleware的好处在于,经过middleware(本例中session函数)能够很简单的给WSGI程序添加新功能。框架
咱们也可见将middleware包装成类,这样,咱们能够经过继承,复用现有的中间件。类中要重载__call__。socket
class Session: def __init__(self, application): self.application = application def __call__(self, environ, start_response): if "superSession" not in environ: import superSession environ["superSession"] = superSession.session() # Options would obviously need specifying return self.application(environ,start_response) application = Session(exampleApplication)
Gunicorn(绿色独角兽)是一个被普遍使用的高性能的Python WSGI UNIX HTTP服务器,移植自Ruby的独角兽(Unicorn )项目,使用pre-fork worker模式,具备使用很是简单,轻量级的资源消耗,以及高性能等特色。函数
Gunicorn 服务器做为wsgi app的容器,可以与各类Web框架兼容(flask,django等),得益于gevent等技术,使用Gunicorn可以在基本不改变wsgi app代码的前提下,大幅度提升wsgi app的性能。
Gunicorn 使用例子:
$ cat myapp.py def app(environ, start_response): data = b"Hello, World!\n" start_response("200 OK", [ ("Content-Type", "text/plain"), ("Content-Length", str(len(data))) ]) return iter([data]) $ gunicorn -w 4 myapp:app [2014-09-10 10:22:28 +0000] [30869] [INFO] Listening at: http://127.0.0.1:8000 (30869) [2014-09-10 10:22:28 +0000] [30869] [INFO] Using worker: sync [2014-09-10 10:22:28 +0000] [30874] [INFO] Booting worker with pid: 30874 [2014-09-10 10:22:28 +0000] [30875] [INFO] Booting worker with pid: 30875 [2014-09-10 10:22:28 +0000] [30876] [INFO] Booting worker with pid: 30876 [2014-09-10 10:22:28 +0000] [30877] [INFO] Booting worker with pid: 30877
Gunicorn 有一个master进程,以及几个的worker进程,master经过pre-fork的方式建立多个worker,跟Nginx的有点像。
以下是master进程fork出worker进程的代码:
def spawn_worker(self): self.worker_age += 1 #建立worker。请注意这里的app 对象并非真正的wsgi app对象,而是gunicorn的app对象; #gunicorn的app对象负责import咱们本身写的wsgi app对象。 worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS, self.app, self.timeout / 2.0, self.cfg, self.log) pid = os.fork() if pid != 0: #父进程,返回后继续建立其余worker,没worker后进入到本身的消息循环 self.WORKERS[pid] = worker return pid # Process Child worker_pid = os.getpid() try: .......... worker.init_process() #子进程,初始化woker,进入worker的消息循环, sys.exit(0) except SystemExit: raise ............
在worker.init_process()函数中,worker中gunicorn的app对象会去import 咱们的wsgi app。也就是说,每一个woker子进程都会单独去实例化咱们的wsgi app对象。每一个worker中的swgi app对象是相互独立、互不干扰的。
manager维护数量固定的worker:
def manage_workers(self): if len(self.WORKERS.keys()) < self.num_workers: self.spawn_workers() while len(workers) > self.num_workers: (pid, _) = workers.pop(0) self.kill_worker(pid, signal.SIGQUIT)
建立完全部的worker后,worker和master各自进入本身的消息循环。
master的事件循环就是收收信号,管理管理worker进程,而worker进程的事件循环就是监听网络事件并处理(如新建链接,断开链接,处理请求发送响应等等),因此真正的链接最终是连到了worker进程上的。
woker有不少种,包括:ggevent、geventlet、gtornado等等。这里主要分析ggevent。
每一个ggevent worker启动的时候会启动多个server对象:worker首先为每一个listener建立一个server对象(注:为何是一组listener,由于gunicorn能够绑定一组地址,每一个地址对于一个listener),每一个server对象都有运行在一个单独的gevent pool对象中。真正等待连接和处理连接的操做是在server对象中进行的。
#为每一个listener建立server对象。 for s in self.sockets: pool = Pool(self.worker_connections) #建立gevent pool if self.server_class is not None: #建立server对象 server = self.server_class( s, application=self.wsgi, spawn=pool, log=self.log, handler_class=self.wsgi_handler, **ssl_args) ............. server.start() #启动server,开始等待连接,服务连接 servers.append(server) .........
上面代码中的server_class其实是一个gevent的WSGI SERVER的子类:
class PyWSGIServer(pywsgi.WSGIServer): base_env = BASE_WSGI_ENV
注意,server_class的参数中s是server用来监听连接的套接字。spawn是gevent的协程池。application便是咱们的wsgi app(通俗点讲就是你用 flask 或者 django写成的app),咱们的app就是经过这种方式交给gunicorn的woker去跑的。 handler_class是gevent的pywsgi.WSGIHandler子类。
真正等待连接和处理连接的操做是在gevent的WSGIServer 和 WSGIHandler中进行的。
最后再来看一下gevent的WSGIServer 和 WSGIHandler的主要实现:
WSGIServer 的start函数里面调用start_accepting来处理到来的连接。在start_accepting里面获得接收到的套接字后调用do_handle来处理套接字:
def do_handle(self, *args): spawn = self._spawn spawn(self._handle, *args)
能够看出,WSGIServer 其实是建立一个协程去处理该套接字,也就是说在WSGIServer 中,一个协程单独负责一个HTTP连接。协程中运行的self._handle函数其实是调用了WSGIHandler的handle函数来不断处理http 请求:
def handle(self): try: while self.socket is not None: result = self.handle_one_request()#处理HTTP请求 if result is None: break if result is True: continue self.status, response_body = result self.socket.sendall(response_body)#发送回应报文 ..............
在handle函数的循环内部,handle_one_request函数首先读取HTTP 请求,初始化WSGI环境,而后最终调用run_application函数来处理请求:
def run_application(self): self.result = self.application(self.environ, self.start_response) self.process_result()
在这个地方才真正的调用了咱们的 app。
总结:gunicorn 会启动一组 worker进程,全部worker进程公用一组listener,在每一个worker中为每一个listener创建一个wsgi server。每当有HTTP连接到来时,wsgi server建立一个协程来处理该连接,协程处理该连接的时候,先初始化WSGI环境,而后调用用户提供的app对象去处理HTTP请求。