Python是一门非常友好的语言,学习成本很低,这也是我很喜欢写Python的原因之一,在与应用端或者是业务端做整合的时候我们经常会将模型或者是数据分析的应用做成可以被直接调用的web服务来提供外部的访问,在web服务搭建这一块,有很多的第三方库可以完成这一任务,这里列举出来我了解的web框架,欢迎补充:
Django: Python Web应用开发框架
Diesel:基于Greenlet的事件I/O框架
Flask:一个用Python编写的轻量级Web应用框架
Cubes:轻量级Python OLAP框架
Kartograph.py:创造矢量地图的轻量级Python框架
Web2py:全栈式Web框架
Pulsar:Python的事件驱动并发框架
Falcon:构建云API和网络应用后端的高性能Python框架
Bottle: 微型Python Web框架
Tornado:异步非阻塞IO的Python Web框架
webpy: 轻量级的Python Web框架
对于我个人而言,Flask是我搭建web服务的首选,很轻量级,使用起来也很方便,简单的应用几乎是没有什么学习成本的,相信使用过的人对于下面这句话都不会陌生:
很简单的依据启动命令就可以将我们自己的应用或者是模块暴露成可被访问、调用的web服务了,很神奇的吧。
说了Flask的很多好处,但是这是否意味着它是完美的了呢?其实并不是,很大程度上需要取决于你的应用复杂程度,今天遇上一个问题就是需要在生产环境里面部署Flask应用服务,之前这个问题我并没有去过多考虑,因为觉得开发环境和生产环境的部署方式是一样的,所以一直也就这么做着项目走过来了后面发现并不是这个样子的,我们来看一个简单的例子:
上图是在windows下面我启动了本地的一个web服务,终端输出的提示信息以及警告信息,之前一直都忽略了这一行信息,其实这里的警告信息很简单就是说:不要使用这种方式在生产环境里面部署你的web服务,如果仅仅是用来开发、测试或者是调试什么的则就无所谓了,那么这是为什么呢?是因为:flask直接用于生产环境无论是处理高并发还是鲁棒性都有所欠缺,一般会配合WGSI容器来进行生产环境的部署。
所以这里就需要针对生产环境的部署去做一套方案了。我是参考了这里的方案,截图如下所示:
我们参考的是【自主部署选项】这里的内容,截图如下所示:
这里文档讲的比较简单,不过也大概看懂了使用的过程是比较简单。
这里自己的实践思路就是【gunicorn+gevent】,首先来简单熟悉一下gevent:
官方解释 greenlet 是轻量级的并行编程,gevent 就是利用 greenlet 实现的基于协程(coroutine)的 python 的网络 library,通过使用greenlet提供了一个在libev事件循环顶部的高级别并发API。即 gevent 是对 greenlet 的高级封装。
下面是从廖老师网站里面拿来的介绍:
上面的内容理解了我们差不多就知道gevent是干什么的了。接下来就需要来了解一下gunicorn了,在了解和介绍gunicorn之前,我们先来看看WSGI是什么,下面是摘自百度百科的介绍:
WSGI介绍:
PythonWeb服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是Python应用程序或框架和Web服务器之间的一种接口,已经被广泛接受, 它已基本达成它的可移植性方面的目标。WSGI 没有官方的实现, 因为WSGI更像一个协议. 只要遵照这些协议,WSGI应用(Application)都可以在任何服务器(Server)上运行, 反之亦然。
WSGI标准在 PEP333中定义并被许多框架实现,其中包括现广泛使用的django、flask框架。
诞生背景:
以前如何选择合适的Web应用程序框架成为困扰Python初学者的一个问题,这是因为,一般而言,Web应用框架的选择将限制可用的Web服务器的选择,反之亦然。那时的Python应用程序通常是为CGI,FastCGI,mod_python中的一个而设计,甚至是为特定Web服务器的自定义的API接口而设计的。WSGI(有时发音作'wiz-gee')是作为Web服务器与Web应用程序或应用框架之间的一种低级别的接口,以提升可移植Web应用开发的共同点。WSGI是基于现存的CGI标准而设计的。
看了好多gunicorn介绍的资料,下面总结的一段话感觉是比较贴切的了:
Gunicorn是一个unix上被广泛使用的高性能的Python WSGI UNIX HTTP Server。和大多数的web框架兼容,并具有实现简单,轻量级,高性能等特点。gunicorn是支持wsgi协议的http服务器,gevent只是它支持的模式之一,是为了解决django、flask这些web框架自带wsgi server性能低下的问题。自带的webserver更多的是测试用途,线上发布时,最好使用高性能的wsgi server或者是联合nginx做uwsgi。
毕竟是第一次接触到的东西,所以必要的背景内容和相关的介绍说明学习是很重要的对于我来说是这样的。初步熟悉和了解之后就可以开始实践了,首选是环境的搭建:
pip install gunicorn
pip install gevent
完成了环境的搭建后,下面是一个简单的实例【myApp.py】:
#!usr/bin/env python
#encoding:utf-8
from __future__ import division
'''
__Author__:沂水寒城
功能: gunicorn+flask Demo
'''
from flask import *
#过滤掉警示信息
import warnings
warnings.filterwarnings("ignore")
app=Flask(__name__)
@app.route("/")
def init():
'''
初始化启动接口
http://localhost:5000/
'''
return u"gunicorn+flask web服务正常启动........"
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000)
启动命令如下所示:
gunicorn -w 10 -b 0.0.0.0:5000 myApp:app
在Linux终端执行上面的命令后就可以看到相应的输出了,这里就不进行展示了,后面会有具体的实例。
下午了解到的启动方式主要有两种,上面的直接命令行启动是第一种方式,第二种方式是基于配置文件的启动,这里就需要编写一个config.py的配置文件来对gunicorn的配置项进行配置了,我们在终端可以简单地通过下面的命令来查看gunicorn所有的配置选项:
gunicorn -h
下面是所有的可选配置项信息:
usage: gunicorn [OPTIONS] [APP_MODULE]
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-m INT, --umask INT A bit mask for the file mode on files written by
Gunicorn. [0]
--worker-connections INT
The maximum number of simultaneous clients. [1000]
--max-requests INT The maximum number of requests a worker will process
before restarting. [0]
--graceful-timeout INT
Timeout for graceful workers restart. [30]
--access-logfile FILE
The Access log file to write to. [None]
--reload-engine STRING
The implementation that should be used to power
:ref:`reload`. [auto]
--preload Load application code before the worker processes are
forked. [False]
-D, --daemon Daemonize the Gunicorn process. [False]
--strip-header-spaces
Strip spaces present between the header name and the
the ``:``. [False]
--certfile FILE SSL certificate file [None]
--log-syslog-to SYSLOG_ADDR
Address to send syslog messages. [udp://localhost:514]
--statsd-prefix STATSD_PREFIX
Prefix to use when emitting statsd metrics (a trailing
``.`` is added, []
-w INT, --workers INT
The number of worker processes for handling requests.
[1]
--max-requests-jitter INT
The maximum jitter to add to the *max_requests*
setting. [0]
--no-sendfile Disables the use of ``sendfile()``. [None]
--reuse-port Set the ``SO_REUSEPORT`` flag on the listening socket.
[False]
--worker-tmp-dir DIR A directory to use for the worker heartbeat temporary
file. [None]
-u USER, --user USER Switch worker processes to run as this user. [0]
--reload Restart workers when code changes. [False]
--chdir CHDIR Chdir to specified directory before apps loading.
[/root/cgb/demo]
--access-logformat STRING
The access log format. [%(h)s %(l)s %(u)s %(t)s
"%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"]
--limit-request-fields INT
Limit the number of HTTP headers fields in a request.
[100]
--ca-certs FILE CA certificates file [None]
--spew Install a trace function that spews every line
executed by the server. [False]
--forwarded-allow-ips STRING
Front-end's IPs from which allowed to handle set
secure headers. [127.0.0.1]
--do-handshake-on-connect
Whether to perform SSL handshake on socket connect
(see stdlib ssl module's) [False]
-R, --enable-stdio-inheritance
Enable stdio inheritance. [False]
--paste STRING, --paster STRING
Load a PasteDeploy config file. The argument may
contain a ``#`` [None]
--proxy-allow-from PROXY_ALLOW_IPS
Front-end's IPs from which allowed accept proxy
requests (comma separate). [127.0.0.1]
--logger-class STRING
The logger you want to use to log events in Gunicorn.
[gunicorn.glogging.Logger]
--keep-alive INT The number of seconds to wait for requests on a Keep-
Alive connection. [2]
-c CONFIG, --config CONFIG
The Gunicorn config file. [None]
--log-level LEVEL The granularity of Error log outputs. [info]
--paste-global CONF Set a PasteDeploy global config variable in
``key=value`` form. [[]]
--pythonpath STRING A comma-separated list of directories to add to the
Python path. [None]
--reload-extra-file FILES
Extends :ref:`reload` option to also watch and reload
on additional files [[]]
--capture-output Redirect stdout/stderr to specified file in
:ref:`errorlog`. [False]
--ciphers CIPHERS SSL Cipher suite to use, in the format of an OpenSSL
cipher list. [None]
--keyfile FILE SSL key file [None]
-k STRING, --worker-class STRING
The type of workers to use. [sync]
--log-config-dict LOGCONFIG_DICT
The log config dictionary to use, using the standard
Python [{}]
--log-syslog-prefix SYSLOG_PREFIX
Makes Gunicorn use the parameter as program-name in
the syslog entries. [None]
--backlog INT The maximum number of pending connections. [2048]
--limit-request-line INT
The maximum size of HTTP request line in bytes. [4094]
-p FILE, --pid FILE A filename to use for the PID file. [None]
-b ADDRESS, --bind ADDRESS
The socket to bind. [['127.0.0.1:8000']]
--disable-redirect-access-to-syslog
Disable redirect access logs to syslog. [False]
--limit-request-field_size INT
Limit the allowed size of an HTTP request header
field. [8190]
-n STRING, --name STRING
A base to use with setproctitle for process naming.
[None]
--error-logfile FILE, --log-file FILE
The Error log file to write to. [-]
--ssl-version SSL_VERSION
SSL version to use. [_SSLMethod.PROTOCOL_SSLv23]
--cert-reqs CERT_REQS
Whether client certificate is required (see stdlib ssl
module's) [0]
-g GROUP, --group GROUP
Switch worker process to run as this group. [0]
--proxy-protocol Enable detect PROXY protocol (PROXY mode). [False]
--threads INT The number of worker threads for handling requests.
[1]
-t INT, --timeout INT
Workers silent for more than this many seconds are
killed and restarted. [30]
--dogstatsd-tags DOGSTATSD_TAGS
A comma-delimited list of datadog statsd (dogstatsd)
tags to append to statsd metrics. []
--suppress-ragged-eofs
Suppress ragged EOFs (see stdlib ssl module's) [True]
--log-config FILE The log config file to use. [None]
--initgroups If true, set the worker process's group access list
with all of the [False]
--check-config Check the configuration. [False]
--log-syslog Send *Gunicorn* logs to syslog. [False]
--log-syslog-facility SYSLOG_FACILITY
Syslog facility name [user]
--statsd-host STATSD_ADDR
``host:port`` of the statsd server to log to. [None]
-e ENV, --env ENV Set environment variable (key=value). [[]]
感觉有很多很多,不过不是说所有的都是需要去手动配置的,我们只需要配置最常使用到的几个就行了,下面是我编写的一个配置文件:
#!usr/bin/env python
#encoding:utf-8
import os
import gevent.monkey
gevent.monkey.patch_all()
import multiprocessing
#开发环境可以打开,生产环境可以
#debug = True
#用于控制errorlog的信息级别,可以设置为debug、info、warning、error、critical
loglevel = 'debug'
#监听地址+端口
bind = "0.0.0.0:5000"
#定义日志存储
if not os.path.exists('log/'):
os.makedirs('log/')
pidfile = "log/gunicorn.pid"
#访问日志
accesslog = "log/access.log"
#错误日志
errorlog = "log/debug.log"
#开启后台运行,默认值为False
daemon = True
#启动的进程数,推荐值为:CPU核数*2+1
workers = multiprocessing.cpu_count()*2+1
#指开启的每个工作进程的模式类型,默认为sync模式,也可使用gevent模式
worker_class = 'gevent'
x_forwarded_for_header = 'X-FORWARDED-FOR'
个人感觉写得是很清晰的了,相应的配置项都已经写上了注释说明了方便具体的改动处理。我们使用第一种启动方式启动,下面是具体的输出截图:
命令:
gunicorn -w 4 -b 172.19.6.213:5000 myApp:app
启动命令中,我们设置了4个进程,从结果来看一共有4个不同的pid编号说明启动了4个不同的进程。关闭可以使用【Ctrl+C】的方式,截图如下所示:
接下来使用第二种启动方式执行,命名如下:
gunicorn --config=config.py API:app
结果截图如下所示:
第二种方式是基于配置文件的启动方式,这里我们启动的同时还生成了日志文件:
首先来看debug.log的内容,因为内容比较多,这里只看最后几行:
然后是看gunicorn.pid的内容:
细心的你是不是发现了什么呢?57523这个pid是不是。
最后看access.log的内容,这是访问日志存储文件,因为我们没有访问接口,所以文件就是空的。
到这里整个的生产环境部署实践差不多就结束了,感觉也是收获很多,学海无涯啊!