图1: CherryPy
CherryPy
可以独立工作,但也会使用于更加复杂的环境,有时候会使用 反向代理
或者用其他服务器来管理程序。
如:
from cherrypy.process.plugins import Daemonizerd = Daemonizer(cherrypy.engine)d.subscribe()
默认 stdin
, stdout
, stderr
是指向 /dev/null
的,但也可以写到日志文件等。
如:
DropPrivileges(cherrypy.engine, uid=1000, gid=1000).subscribe()
启动时将pid吸入一个pidfile,退出时删除:
PIDFile(cherrypy.engine, '/var/run/myapp.pid').subscribe()
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
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
在将 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)
使用举例:
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()
使用举例(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
使用举例(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
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})
以一个配置文件开始(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())
这样只要启动两个程序,每个的端口不同,则可以在两个服务器之间进行负载均衡。
至此,第一波学习内容就到这里了,接下来是进行一些实践并看看源码。
作者简介:
朱春来(Leslie Zhu),金融工程师,毕业于西安电子科技大学, 喜欢历史,喜欢编程. 日常在GNU/Linux环境下进行C/C++、Python开发,对Common Lisp、Node.js、金融等感兴趣。可以通过邮箱(pythonisland@gmail.com)联系他,或者直接在他的个人主页上留言.
访问朱春来(Leslie Zhu)的个人主页(http://lesliezhu.github.com)