开发python web站点时,本地用gunicorn,werkzeug和quixote来搭建环境。
其实这是两种方式:
①gunicorn+quixote
②werkzeug+quixote
Gunicorn是一个python wsgi的 web-server服务器
werkeug则是一个wsgi的lib工具。
quixote:通俗来说,主要功能就是转发请求,模板渲染神马功能。 主要死转发请求!!
Ok,那理解起来就很简单了: gunicorn和werkzeug负责启动服务,监听请求,并把请求转给quixote。 quixote负责寻找合适的fun或model来处理这个请求.
(当然后续还有模板渲染,暂时先不考虑,我们先把前边请求到来的流程搞定.)
------------------------------------------------------------------------------------------
gunicorn和werkzeug都是wsgi规范的。那我们所有操作都围绕该规范来就是了.这个类比一下php,php中一般都是所有请求都转到一个index.php中处理。
同样的;wsgi也是,其是所有请求到来时,都转到一个函数中去处理。这个函数是所有请求的统一入口!
ok,为了实现以上几点:
①你要定义一个入口函数。
②你要把所有请求都转到该入口函数来处理。
如果全部用面向对象方式来做:
①入口函数我们封装到一个类中,那为了可以用类名直接调用这个函数,我们需要将之封装到__call__()函数中.
class RealApp(object): def __init__(self, rns): pass def cyrildou(environ, start_response): data = "Hello, World!\n" start_response("200 OK", [ ("Content-Type", "text/plain"), ("Content-Length", str(len(data))) ]) return iter([data]) def __call__(self, env, start_response): cyrildou(env, start_response)
②如何将所有请求都转到这个函数中呢?gunicorn和werkzeug都有自己的封装。 但是不管如何封装,都得符合wsgi规范不是?
其实规范也简单,对于上述①中的入口函数,其__call__()函数必须接收env和start_response这两个参数。
说白一点:第一个参数env是用户发送请求过来的所有请求信息和环境信息。 而start_response则是一个回调函数,其可以理解成:是控制请求传入的web-server的一个函数,用于获取返回的header。 所以我们在这个处理函数__call__中需要通过这个start_response()函数来返回请求的http header···
那么:按照协议,所有的wsgi的web-server或者lib都得符合这种样式来提供给后边的app用。
总结一下:
①我们要建自己的app,要求其接收2个参数,一个是传入info,一个是设置response header的回调函数(这个函数也是开启返回的函数,说白了:在处理请求后只有先调用该函数才算是开始返回信息!)
②任何web-server或者lib都是调用上边①中的app来处理所有请求的.
---------------------------------------------------------------------------------------------
ok,那下一步看一下gunicorn和werkzeug是如何转发请求的.
这里就不分析源码了,只说明如何调用。
①对于werkzeug:
非常简单,只需要调用run_simple()函数即可
demo_wsgi.py
application = RealApp() try: from werkzeug import run_simple except ImportError: raise Exception("please install werkzeug") run_simple(devsite, port, application, use_reloader=True, use_debugger=True, use_evalex=True, processes=2, extra_files=extra_files)
最关键的几个参数:参数1,2指定了监听的domian/ip 和port。 而参数3则指定了处理请求的入口app(这里就是上边我们创建的app类对象)
所以:通过参数3就将请求和处理函数给关联到了一起。
如上之后,我们就可以执行命令 `python demo_wsgi.py`来执行启动监听了.命令行中就可以看到各种info啦·····
②对于gunicorn
我们也用面向对象的方式来做。这里直接贴代码了
class DemoApplication(Application): #初始化时调用 def init(self, parser, opts, args): self.app_uri = args[0] sys.path.insert(0, os.getcwd()) #运行时调用 def load(self): try: #important! return util.import_app(self.app_uri) except Exception, e: exc = sys.exc_info() return make_tb_echo_app(exc) def main(): sys.argv += ['-c', os.path.join(os.path.dirname(__file__), 'gunicorn_config.py'),'demo_wsgi',] DemoApplication("%prog [OPTIONS] APP_MODULE").run() if __name__ == '__main__': main()
本质上:我们就是从Application继承一个自己的Application类,而后调用run()函数运行之即可。
当然啦:你可能会有很多疑问,先别着急,一步步来。
这个自己的Application类中最重要的2个函数是init()和load()函数:其中init()是创建这个类对象时调用的,而load则是执行run()函数时调用的。
所以执行run()函数来启动之,其对我们而言:就是调用了load()函数,所以我们要做的就是在load()函数中来导入app即可! 这样Application会将请求都转到这个app中去执行!!
说白话一点:我们要做的仅仅是重新实现Application类的load()函数,让其返回实际处理请求的application即可!(这里的application就是前边RealApp)
那还有问题:
①如何找到处理请求的application的呢?
②配置如何加载的?监听哪个端口?哪个域名?。。。
对于①:
我们导入application是用的gunicorn中的系统函数:util.import_app(model)
这个函数顾名思义就是:导入一个app,而参数就是一个模块的名字. 其在实现上就是用的__import__()方法。所以实际上这里写的应该就是最终python脚本的模块名字。而后gunicorn就会来到这个python脚本中去查找application的变量,并将之作为处理请求的app来加载使用!
所以这里注意了:使用该种方式时:最终处请求的app的对象名字必须是:application!!!! 否则gunicorn会找不到!!!
对于②
这个是通过加参数 -c filename来实现的!
这个配置文件filename也应该是一个python脚本,其中含有一些指定的变量和对应的你指定的值.举个例子如下:
daemon = False debug = True loglevel = 'error' workers = 2 keepalive = 10 timeout = 600 bind = '0.0.0.0:%s' % port proc_name = 'demo %s:%s' % (devsite, port)
============================================================================
那上边通过这两种不同的组合,我们都已经在一个函数中接收到用户的请求啦,下一步是处理请求。在上边例子中我们给出了一个简单的处理:不管任何请求到来,都返回200, hello world!
但是实际上不可能这样的:肯定要先解析用户传过来的请求,依据此来决定如何处理
那就要首先拿到传过来的信息。
我们知道:处理函数的样式如下:
def dispatch(environ, start_response):其中environ是请求和环境信息的集合。从中我们如何拿到我们需要需要的信息呢?
最简单的:用户请求如下: www.baidu.com/a/b/c?curosr=123 是一个post请求,body中信息为 file=amc
那我们如何拿到GET[],POST],HEADER[]等信息呢?php简单,就有这些系统全局数组。但是这里木有貌似··
另外一个问题:我们如何转发请求呢? /a/b/c /a/b/s 应该是不同的path,对应不同请求···这里用的就是quixote来转发到不同函数来处理啦!!O(∩_∩)O哈哈~
类比php,application就类比于index.php入口脚本,而后边的实际处理函数就是php的各个实际action!!
那当我们进入application()这个请求处理入口函数之后,下一步就该让quixote接手了。应该让它来把不同的请求转到不同的函数中去处理!
那quixote需要做哪些工作呢?
①其会解析wsgi穿过的environ信息,将之封装为Request对象,
②将①创建的Request对象交给后边的实际处理函数处理并返回信息给用户即可!!
那quixote是如何实现上述2点的呢?直接贴一段代码吧:
def __call__(self, env, start_response): quixote.publish._publisher = self.publisher input = env['wsgi.input'] request = self.publisher.create_request(input, env) output = self.publisher.process_request(request, env) response = request.response status = "%03d %s" % (response.status_code, response.reason_phrase) headers = response.generate_headers() start_response(status, headers) return str(output)嗯 说白了:
我们首先拿到用户的输入 input=env['wsgi.input']
而后create_request来封装生成一个request对象,而后process_request处理之获取返回的body.
最后就调用回调函数start_response()来启动返回的过程并返回http header,最后返回http body
从上可见:quixote功能实现最重要的是使用了quixote中Publisher这个类对象,其负责处理所有请求的转发等~~
那我们在使用时:只需要创建一个Publisher对象,并按照指定格式调用即可! 而创建Publisher只需要传入唯一的一个参数:指定root namespace!!!
说白了就是指定一下controller的目录!
=============================================================
如上之后:我们框架就搭建完毕啦,下一步就是只负责处理各种请求即可!!!