利用Gunicorn和flask搭建web-server
之前用tornado搭服务器,但是tornado只支持多线程。众所周知python的多线程性能是很差的。
要上多进程,Gunicorn加flask这个组合刚好满足需求。而且Gunicorn支持进程意外退出后重启,还支持很多灵活的配置,综合来看是很不错的组合方案。
安装
需要安装Gunicorn和flask,另外还有gevent,具体方案可以百度。
配置
Gunicorn启动flask典型的例子需要两个文件:一个配置文件和一个python文件。配置文件定义给Gunicorn的参数(实际上这些参数也可通过命令行传入),python文件定义flask服务。
我写了个例子,我的配置文件如下:
# 监听IP和端口,也可以写'0.0.0.0:port',则在所有网卡上监听
bind = '192.168.74.205:9999'
# 服务无响应(不返回)被重启的时间阈值
timeout = 30
worker_class = 'gevent'
# This refers to the number of clients that can be waiting to be served.
backlog = 512
# 工作目录
chdir = './'
# 并行工作进程数,默认 1
workers = 5
# 指定每个进程的线程数, 默认 1
threads = 2
# 检测到代码改动是否重启服务
reload = True
# 将服务放入后台不受终端的影响
daemon = False
# stdout/stderr 是否重定向到错误日志
capture_output = False
# 日志相关
loglevel = 'info'
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
# 连接的IP等信息会记入accesslog日志
accesslog = "./gunicorn_access.log"
# 包括重启在内的一些信息会记录在错误日志
errorlog = "./gunicorn_error.log"
用flask实现server端业务
from flask import Flask
from flask import request
import os
app = Flask(__name__)
@app.route('/', methods=['POST'])
def fp_check():
try:
post_data = request.data
input_dict = eval(post_data)
print('------in server------')
print( 'input_dict:{}'.format(input_dict) )
'''
# do something here
'''
result = { "result": 0, "msg": 'ok'}
print('out result:{}'.format(result))
return str(result)
except Exception as e:
logger.error("Error in fp_check: " + str(e))
测试代码
# -*- coding: utf-8 -*-
import json
import base64
import requests
import time
import os
url = "http://192.168.74.205:9999/"
headers = {"appId":"001", "token":"D6A7", "requestId":"789", "requestTime":"2018-03-15 15:37:54"}
def get():
#print image_base64_data
body = {"comments":"XXXX"}
response = requests.post(url=url, data=json.dumps(body), headers=headers)
print('-----in test------')
print(response.text)
if __name__ == '__main__':
get()
启动服务
现在我有三个文件:配置文件名为my.conf,服务端代码文件为mymain.py,测试代码文件为test.py。
启动服务命令
# gunicorn -c my.conf mymain:app &
这样所有的服务就都起在后台了,使用ps命令查看,结果如下:
刚好5个进程,和配置文件里一致。想杀掉进程的话,直接杀父进程22179就行。
我配置文件里配置了检测到代码改动自动重启,我试了下确实有用,调试的时候很方便呀,可以边改边测,不用手动去启停服务了。
测试
启动test.py测试,结果如下:
# python3 test.py
------in server------
input_dict:{'comments': 'XXXX'}
out result:{'result': 0, 'msg': 'ok'}
-----in test------
{'result': 0, 'msg': 'ok'}
这只是一个例子,实际生产中可以用jason传递base64等复杂的数据。
用tornado实现web-server
功能基本一样。我这里没做实际有意义的事,仅仅在这里示意用法。实际上flask可以做和tornado同样的事情。
import tornado.web
import tornado.httpserver
import tornado.ioloop
import os
class MyHandler( tornado.web.RequestHandler ):
executor = ThreadPoolExecutor( 10 ) # max 20 thread pool
@run_on_executor
def post(self):
print("============in server=================")
if 1:
jason_dict = eval(str(self.request.body))
print('jason_dict:{}'.format(jason_dict))
resultDic = {}
resultDic["result_code"] = 0
resultDic["msg"] = "ok"
self.write(resultDic)
print('resultDic:{}'.format(resultDic))
return
def main_start():
try:
# tornado.options.parse_command_line()
app = tornado.web.Application(
handlers=[
(r"/", MyHandler),
]
)
http_server = tornado.httpserver.HTTPServer( app )
port = 9999
http_server.listen( port )
tornado.ioloop.IOLoop.instance().start()
except Exception as e:
logger.exception( "Stop the service:{}".format(e) )
if __name__ == "__main__":
main_start()
总结
WSGI的功能类似于CGI,CGI是通用的,WSGI仅仅是python语言的一个web调用规范。
flask是一个支持WSGI的web框架
gunicorn是一个独立的WSGI server(其他的都是模块)。可以支持gevent的woker模式。
tornado是python的非阻塞异步框架,包含了WSGI server和web框架。
也就是说tornado是一个比较大的概念,它自己可以做“gunicorn+flask”做的事。但是tornado缺少进程管理功能。tornado也可以作为WSGI server调用flask的。
综合来说,“gunicorn+flask”(实际上还有gevent)性能很好,又支持进程管理(特别是进程意外退出后的重启),非常值得使用。