CherryPy: 一个极简主义Python Web框架(6)——部署

CherryPy: 一个极简主义Python Web框架(6)——部署

图1: CherryPy

目录

1 前言2 以守护进程形式运行3 以不同用户身份运行4 PID文件5 通过Supervisord管理6 SSL支持7 WSGI服务器7.1 嵌入到其他WSGI框架7.2 Tornado7.3 Twisted7.4 uwsgi8 虚拟主机9 反向代理9.1 Apache9.2 Nginx10 后记11 参考

1 前言

CherryPy 可以独立工作,但也会使用于更加复杂的环境,有时候会使用 反向代理 或者用其他服务器来管理程序。

2 以守护进程形式运行

如:

from cherrypy.process.plugins import Daemonizerd = Daemonizer(cherrypy.engine)d.subscribe()

默认 stdin, stdout, stderr 是指向 /dev/null 的,但也可以写到日志文件等。

3 以不同用户身份运行

如:

DropPrivileges(cherrypy.engine, uid=1000, gid=1000).subscribe()
4 PID文件

启动时将pid吸入一个pidfile,退出时删除:

PIDFile(cherrypy.engine, '/var/run/myapp.pid').subscribe()
5 通过Supervisord管理

supervisord 是一个强大的进程控制和管理工具,在进程监控方面进行了大量的工作,下面是关于 supervisor 配置内容:

[unix_http_server]file=/tmp/supervisor.sock[supervisord]logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)logfile_backups=10           ; (num of main logfile rotation backups;default 10)loglevel=info                ; (log level;default info; others: debug,warn,trace)pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)nodaemon=false               ; (start in foreground if true;default false)minfds=1024                  ; (min. avail startup file descriptors;default 1024)minprocs=200                 ; (min. avail process descriptors;default 200)[rpcinterface:supervisor]supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface[supervisorctl]serverurl=unix:///tmp/supervisor.sock[program:myapp]command=python server.pyenvironment=PYTHONPATH=.directory=.

这可以通过 server.py 来控制程序入口:

import cherrypyclass Root(object):    @cherrypy.expose    def index(self):        return "Hello World!"cherrypy.config.update({'server.socket_port': 8090,                        'engine.autoreload_on': False,                        'log.access_file': './access.log',                        'log.error_file': './error.log'})cherrypy.quickstart(Root())

启动:

$ supervisord -c supervisord.conf$ supervisorctl update

关闭:

$ supervisorctl shutdown
6 SSL支持

1).生成私有密钥

$ openssl genrsa -out privkey.pem 2048

2).生成证书

$ openssl req -new -x509 -days 365 -key privkey.pem -out cert.pem

3).使用SSL支持

内置SSL模块

cherrypy.server.ssl_module = 'builtin'

使用 pyOpenSSL

$ pip install cython, pyOpenSSL

4).添加配置内容

cherrypy.server.ssl_certificate = "cert.pem"cherrypy.server.ssl_private_key = "privkey.pem"

5).如果有证书链,则可以使用:

cherrypy.server.ssl_certificate_chain = "certchain.perm"

6).启动 CherryPy

7 WSGI服务器

7.1 嵌入到其他WSGI框架

在将 CherryPy 嵌入第三方WSGI服务器时候的规则:

如果依赖于 main 频道发布消息,则需要在其他框架的主循环中找到发布消息的方式启动 CherryPy engine, 将发布消息到 start 频道, cherrypy.server.unsubscribe()关闭 CherryPy engine, 将发布消息到 stop 频道 cherrypy.server.unsubscribe()不要调用 cherrypy.engine.block()禁止内置的HTTP服务器,因为不会使用了 cherrypy.server.unsubscribe()禁止自动重载 cherrypy.config.update({'engine.autoreload.on': False})禁止 CherryPy 信号处理, cherrypy.engine.signals.subscribe()使用 embedded 环境变量 cherrypy.config.update({'environment': 'embedded'})

整体而言,要 嵌入 则需要禁止下列方面:

标准输出日志(Stdout logging)自动重载(Autoreloader)配置检查(Configuration checker)登陆错误头部(Headers logging on error)跟踪错误(Tracebacks in error)dispatch错误(Mismatched params error during dispatching)信号(SIGHUP, SIGTERM)

7.2 Tornado

使用举例:

import cherrypyclass Root(object):    @cherrypy.expose    def index(self):        return "Hello World!"if __name__ == '__main__':    import tornado    import tornado.httpserver    import tornado.wsgi    # our WSGI application    wsgiapp = cherrypy.tree.mount(Root())    # Disable the autoreload which won't play well    cherrypy.config.update({'engine.autoreload.on': False})    # let's not start the CherryPy HTTP server    cherrypy.server.unsubscribe()    # use CherryPy's signal handling    cherrypy.engine.signals.subscribe()    # Prevent CherryPy logs to be propagated    # to the Tornado logger    cherrypy.log.error_log.propagate = False    # Run the engine but don't block on it    cherrypy.engine.start()    # Run thr tornado stack    container = tornado.wsgi.WSGIContainer(wsgiapp)    http_server = tornado.httpserver.HTTPServer(container)    http_server.listen(8080)    # Publish to the CherryPy engine as if    # we were using its mainloop    tornado.ioloop.PeriodicCallback(lambda: cherrypy.engine.publish('main'), 100).start()    tornado.ioloop.IOLoop.instance().start()
7.3 Twisted

使用举例(cptw.py):

import cherrypyfrom twisted.web.wsgi import WSGIResourcefrom twisted.internet import reactorfrom twisted.internet import task# Our CherryPy applicationclass Root(object):    @cherrypy.expose    def index(self):        return "hello world"# Create our WSGI app from the CherryPy applicationwsgiapp = cherrypy.tree.mount(Root())# Configure the CherryPy's app server# Disable the autoreload which won't play wellcherrypy.config.update({'engine.autoreload.on': False})# We will be using Twisted HTTP server so let's# disable the CherryPy's HTTP server entirelycherrypy.server.unsubscribe()# If you'd rather use CherryPy's signal handler# Uncomment the next line. I don't know how well this# will play with Twisted however#cherrypy.engine.signals.subscribe()# Publish periodically onto the 'main' channel as the bus mainloop would dotask.LoopingCall(lambda: cherrypy.engine.publish('main')).start(0.1)# Tie our app to Twistedreactor.addSystemEventTrigger('after', 'startup', cherrypy.engine.start)reactor.addSystemEventTrigger('before', 'shutdown', cherrypy.engine.exit)resource = WSGIResource(reactor, reactor.getThreadPool(), wsgiapp)

运行:

$ twistd -n web --port 8080 --wsgi cptw.wsgiapp
7.4 uwsgi

使用举例(mymod.py):

import cherrypy# Our CherryPy applicationclass Root(object):    @cherrypy.expose    def index(self):        return "hello world"cherrypy.config.update({'engine.autoreload.on': False})cherrypy.server.unsubscribe()cherrypy.engine.start()wsgiapp = cherrypy.tree.mount(Root())

运行:

$ uwsgi --socket 127.0.0.1:8080 --protocol=http --wsgi-file mymod.py --callable wsgiapp
8 虚拟主机

CherryPy 支持 虚拟主机 ,使用举例:

import cherrypyclass Root(object):    def __init__(self):        self.app1 = App1()        self.app2 = App2()class App1(object):    @cherrypy.expose    def index(self):        return "Hello world from app1"class App2(object):    @cherrypy.expose    def index(self):        return "Hello world from app2"if __name__ == '__main__':    hostmap = {        'company.com:8080': '/app1',        'home.net:8080': '/app2',    }    config = {        'request.dispatch': cherrypy.dispatch.VirtualHost(**hostmap)    }    cherrypy.quickstart(Root(), '/', {'/': config})
9 反向代理

9.1 Apache

9.2 Nginx

以一个配置文件开始(under /etc/nginx/conf.d/):

upstream apps {   server 127.0.0.1:8080;   server 127.0.0.1:8081;}gzip_http_version 1.0;gzip_proxied      any;gzip_min_length   500;gzip_disable      "MSIE [1-6]\.";gzip_types        text/plain text/xml text/css                  text/javascript                  application/javascript;server {   listen 80;   server_name  www.example.com;   access_log  /app/logs/www.example.com.log combined;   error_log  /app/logs/www.example.com.log;   location ^~ /static/  {      root /app/static/;   }   location / {      proxy_pass         http://apps;      proxy_redirect     off;      proxy_set_header   Host $host;      proxy_set_header   X-Real-IP $remote_addr;      proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;      proxy_set_header   X-Forwarded-Host $server_name;   }}

运行:

$ sudo service nginx stop$ sudo service nginx start

upstream 配置部分定义了地址,在不同server之间可以进行 负载均衡 :

upstream apps {   server 127.0.0.1:8080;   server 127.0.0.1:8081;}

然后在程序中 支持代理:

import cherrypyclass Root(object):    @cherrypy.expose    def index(self):        return "hello world"if __name__ == '__main__':    cherrypy.config.update({        'server.socket_port': 8080,        'tools.proxy.on': True,        'tools.proxy.base': 'http://www.example.com'    })    cherrypy.quickstart(Root())

这样只要启动两个程序,每个的端口不同,则可以在两个服务器之间进行负载均衡。

10 后记

至此,第一波学习内容就到这里了,接下来是进行一些实践并看看源码。

11 参考

http://www.cherrypy.org/https://github.com/cherrypy/cherrypyhttps://bitbucket.org/cherrypy/cherrypy/wiki/Homehttp://en.wikipedia.org/wiki/CherryPyhttp://tools.ietf.org/html/rfc7231http://en.wikipedia.org/wiki/Web_Server_Gateway_Interfacehttp://legacy.python.org/dev/peps/pep-0333/https://cherrypy.readthedocs.org/en/latest/http://cherrypy.readthedocs.org/en/latest/tutorials.htmlhttps://www.digitalocean.com/community/tutorials/how-to-deploy-python-wsgi-applications-using-a-cherrypy-web-server-behind-nginxhttps://cherrypy.readthedocs.org/en/latest/tutorials.html#tutorialshttp://en.wikipedia.org/wiki/Session_(computer_science)http://www.ibm.com/developerworks/library/ws-restful/index.htmlhttp://zh.wikipedia.org/zh/RESThttp://jquery.com/https://cherrypy.readthedocs.org/en/latest/basics.html#basicshttp://en.wikipedia.org/wiki/Common_Log_Formathttp://en.wikipedia.org/wiki/HTTP_status_codehttps://cherrypy.readthedocs.org/en/latest/advanced.html#advanced


作者简介:

朱春来(Leslie Zhu),金融工程师,毕业于西安电子科技大学, 喜欢历史,喜欢编程. 日常在GNU/Linux环境下进行C/C++、Python开发,对Common Lisp、Node.js、金融等感兴趣。可以通过邮箱(pythonisland@gmail.com)联系他,或者直接在他的个人主页上留言.

访问朱春来(Leslie Zhu)的个人主页(http://lesliezhu.github.com)

CherryPy: 一个极简主义Python Web框架(6)——部署

相关文章:

你感兴趣的文章:

标签云: