为了实现并发,python常见的web部署搭配nginx + gunicorn, flask + gunicorn
本文介绍gunicorn的配置和使用。
pip install gunicorn
gunicorn -h # 查看使用的命令
直接使用 gunicorn 命令可能会报,可以查找到gunicorn文件以绝对路径
添加环境变量 vi /etc/profile
最底下添加:PATH="$PATH:/usr/local/bin"
export PATH
引用环境变量:source /etc/profile
2.1. 配置flask路由 (my_service.py )
# my_service.py
# !/usr/bin/env python
# -*- coding:utf8 -*-
# Desc: http路由
import json
from flask import Flask
from MyLogObj import * #日志
app = Flask(__name__)
app.config['DEBUG'] = True
my_methods=["GET","POST"]
@app.route('/')
def index():
print("--------1----------")
return '请求成功'
@app.route('/error')
def index_error():
print("--------error----------")
a = 'assss'
json.loads(a)
return '请求失败'
@app.route('/logtest')
def index_logtest():
try:
print("--------logtest----------")
a = 'assss111'
json.loads(a)
except Exception as e:
mlog.error('read conffile[%s] except' % CONF_FILE)
mlog.info('read conffile[%s] except info ' % CONF_FILE)
return '请求失败logtest'
if __name__ == '__main__':
app.run(host='127.0.0.1',port=8001)
# -D后台启动,第一个my_service指的是my_service.py文件,第二个指的是py里flask应用的名字;
gunicorn -D -w 4 -b 0.0.0.0:8000 my_service:app
3.1 全局配置文件
# config.ini
[MySQL]
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWRD=pwd123
DB_DATABASE=dbtest
MAX_USAGE=10
MIN_CACHED=1
MAX_CACHED=10
CHARSET=utf8
[RUN]
thread=10
processor=20
[LOG_CONF]
log_level = DEBUG
stdin=0
datefmt = %a, %d %b %Y %H:%M:%S
log_file = log.log
maxSize = 10*1024*1024
3.2 读取配置文件
# MySetting.py
# !/usr/bin/env python
# -*- coding=utf-8 -*-
import platform
import cgi
import configparser
#错误码
SUCCESS = 0 #成功
PARA_FAILED = 1 #参数错误
USER_INFO_ERROR = 3 #用户信息错误
Access_Control ='http://localhost:8001'
if platform.system() == 'Linux':
base_path = '/home/guntest/'
CONF_FILE = base_path + 'conf/config.ini'
Dir_partition = '/'
UPLOADS_PATH=base_path + 'uploads/'
IMG_PATH=base_path + 'img/'
else:
base_path = 'G:\\guntest\\'
CONF_FILE = base_path + 'conf\\config.ini'
Dir_partition = '\\'
UPLOADS_PATH = base_path + 'uploads\\'
IMG_PATH=base_path + 'img\\'
3.3 配置flask路由 (gunicorn_service.py )
# gunicorn_service.py
# !/usr/bin/env python
# -*- coding:utf8 -*-
# Desc: linux start self service by gunicorn
# !/usr/bin/env python
# -*- coding:utf8 -*-
from werkzeug.middleware.proxy_fix import ProxyFix
from my_service import *
import gevent
app.wsgi_app = ProxyFix(app.wsgi_app)
3.4 配置 my-gun.conf
import os
bind = '127.0.0.1:8001'
#bind = '0.0.0.0:8001'
workers = 2
user="root"
worker_class="gevent" #sync, gevent
backlog = 2048
daemon = True
debug = True
proc_name = 'gunicorn-self.proc'
# 服务进程
pidfile = '/home/zyy/log/my-gunicorn.pid'
# 访问日志路径
accesslog = '/home/zyy/log/gunicorn_access.log'
# 错误信息日志路径
errorlog = '/home/zyy/log/gunicorn_error.log'
# 设置日志记录水平
loglevel = 'info' #warning
3.5 日志配置
#MyLogObj.py
#globls
from MyLog import *
mlog = my_logger().GetInstance(None, "guntest-service")
# MyLog.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#Author: zyy
#Date:2023-06-18
#Desc:log class
import logging
from MySetting import *
instance_logger = None
class my_logger:
def __init__(self):
pass
def GetInstance(self, confile, modname):
global instance_logger
if None == instance_logger:
if None == confile or '' == confile:
confile = CONF_FILE
conf = configparser.ConfigParser()
conf.read(confile)
file_path = "%slog%s%s"%(base_path, Dir_partition, conf.get('LOG_CONF', 'log_file'))
logger=logging.getLogger(modname)
fh = logging.FileHandler(file_path)
stdin = conf.get('LOG_CONF', 'stdin')
if stdin == '1':
ch = logging.StreamHandler()
level = conf.get('LOG_CONF','log_level')
if level == None or level == '' or level == 'DEBUG':
logger.setLevel(logging.DEBUG)
fh.setLevel(logging.DEBUG)
if level == 'INFO':
logger.setLevel(logging.INFO)
fh.setLevel(logging.INFO)
if level == 'WARNING':
logger.setLevel(logging.WARNING)
fh.setLevel(logging.WARNING)
if level == 'ERROR' :
logger.setLevel(logging.ERROR)
fh.setLevel(logging.ERROR)
if level == 'CRITICAL':
logger.setLevel(logging.CRITICAL)
fh.setLevel(logging.CRITICAL)
formatter = logging.Formatter("[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s]:%(message)s")
if stdin == '1':
ch.setFormatter(formatter)
fh.setFormatter(formatter)
logger.addHandler(fh)
if stdin == '1':
logger.addHandler(ch)
instance_logger = logger
return instance_logger
if __name__ == '__main__':
log1 = my_logger().GetInstance(None, "log1")
log2 = my_logger().GetInstance(None, "log2")
log3 = my_logger().GetInstance(None, "log3")
log1.debug('this is debug')
log2.debug('this is debug')
3.6 启动文件 start.sh (chmod 777 start.sh)
#!/bin/sh
#coding:utf8
#gunicorn env
source /etc/profile
SERVER_HOME="/home/zyy/zyy_test"
echo '---1----'
SELF_SERVER_EXECUTE="my-gun.conf my_service:app"
SELF_SERVER_NAME="my_service"
SELF_EXECUTE="gunicorn -c my-gun.conf my_service:app"
echo '---2----'
check_self_pid()
{
echo ` ps -efww | grep -v 'grep' | grep "${SELF_SERVER_EXECUTE}" | awk '{print $2}' `
}
start_self()
{
pid=$(check_self_pid)
if [ -n "$pid" ]; then
echo "Warning: $SELF_SERVER_NAME already started! (pid: $pid)"
else
echo -n "Starting $SELF_SERVER_NAME ..."
cd $SERVER_HOME
$SELF_EXECUTE
sleep 1
pid=$(check_self_pid)
if [ -n "$pid" ]; then
echo "(pid: $pid) [OK]"
else
echo "[Failed]"
fi
fi
}
start()
{
start_self
}
stop_self()
{
pid=$(check_self_pid)
if [ -n "$pid" ]; then
echo -n "Stopping $SELF_SERVER_NAME ...(pid: $pid)"
kill $pid &>/dev/null
sleep 1
pid=$(check_self_pid)
if [ -z "$pid" ]; then
echo "[OK]"
else
echo "[Failed]"
fi
else
echo "Warning: $SELF_SERVER_NAME is not running"
fi
}
stop()
{
stop_self
}
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart')
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
3.7 启动
./start.sh start #启动
./start.sh restart #重启
./start.sh stop #停止
4.1 参数
-c CONFIG : CONFIG,配置文件的路径,通过配置文件启动;生产环境使用;
-b ADDRESS : ADDRESS,ip加端口,绑定运行的主机;
-w INT, --workers INT:用于处理工作进程的数量,为正整数,默认为1;
-k STRTING, --worker-class STRTING:要使用的工作模式,默认为sync异步,可以下载eventlet和gevent并指定
--threads INT:处理请求的工作线程数,使用指定数量的线程运行每个worker。为正整数,默认为1。
--worker-connections INT:最大客户端并发数量,默认情况下这个值为1000。
--backlog int:未决连接的最大数量,即等待服务的客户的数量。默认2048个,一般不修改;
-p FILE, --pid FILE:设置pid文件的文件名,如果不设置将不会创建pid文件
--access-logfile FILE : 要写入的访问日志目录
--access-logformat STRING:要写入的访问日志格式
--error-logfile FILE, --log-file FILE : 要写入错误日志的文件目录。
--log-level LEVEL : 错误日志输出等级。
--limit-request-line INT : HTTP请求头的行数的最大大小,此参数用于限制HTTP请求行的允许大小,默认情况下,这个值为4094。值是0~8190的数字。
--limit-request-fields INT : 限制HTTP请求中请求头字段的数量。此字段用于限制请求头字段的数量以防止DDOS攻击,默认情况下,这个值为100,这个值不能超过32768
--limit-request-field-size INT : 限制HTTP请求中请求头的大小,默认情况下这个值为8190字节。值是一个整数或者0,当该值为0时,表示将对请求头大小不做限制
-t INT, --timeout INT:超过这么多秒后工作将被杀掉,并重新启动。一般设定为30秒;
--daemon: 是否以守护进程启动,默认false;
--chdir: 在加载应用程序之前切换目录;
--graceful-timeout INT:默认情况下,这个值为30,在超时(从接收到重启信号开始)之后仍然活着的工作将被强行杀死;一般使用默认;
--keep-alive INT:在keep-alive连接上等待请求的秒数,默认情况下值为2。一般设定在1~5秒之间。
--reload:默认为False。此设置用于开发,每当应用程序发生更改时,都会导致工作重新启动。
--spew:打印服务器执行过的每一条语句,默认False。此选择为原子性的,即要么全部打印,要么全部不打印;
--check-config :显示现在的配置,默认值为False,即显示。
-e ENV, --env ENV: 设置环境变量;
4.2 配置文件参数
# gunicorn.conf
# 并行工作进程数
workers = 4
# 指定每个工作者的线程数
threads = 2
# 监听内网端口5000
bind = '127.0.0.1:5000'
# 设置守护进程,将进程交给supervisor管理
daemon = 'false'
# 工作模式协程
worker_class = 'gevent'
# 设置最大并发量
worker_connections = 2000
# 设置进程文件目录
pidfile = '/var/run/gunicorn.pid'
# 设置访问日志和错误信息日志路径
accesslog = '/var/log/gunicorn_acess.log'
errorlog = '/var/log/gunicorn_error.log'
# 设置日志记录水平
loglevel = 'warning'
4.3 由于windows平台不支持gunicorn,gunicorn安装成功后,启动会出现
‘ImportError: No module named _curses / fcntl’错误;需要下载fcntl.py文件;
参考:
python之gunicorn的配置_modulenotfounderror: no module named 'pwd_黑色小米粥的博客-CSDN博客