Flask 服务需要新建线程跑定时任务,用 gunicorn+gevent 部署,用 supervisor 守护进程,因为无法杀死子进程,它会成为孤儿进程占用端口,需要手动杀死,否则 supervisor 再重启该服务时会报错。
经过验证,实际和 Flask 无关,只不过 Flask 是主进程,占用了端口。
主线程会等待子线程结束才结束。
安装
pip install flask
pip install schedule
app.py
import time
import logging
import threading
import schedule
from flask import Flask
logger = logging.getLogger()
app = Flask(__name__)
@app.route('/')
def ping():
return 'pong\n'
def run():
def job():
logger.warning(str(time.time()))
schedule.every(5).seconds.do(job)
while True:
schedule.run_pending()
time.sleep(1)
threading.Thread(target=run).start()
if __name__ == '__main__':
app.run()
app.conf
[program:app]
directory=/data/all_data/test ; 工作路径
command=/Envs/test/bin/gunicorn -b 127.0.0.1:5001 -k gevent -w 2 app:app ; 启动命令
autostart=true
autorestart=true
startsecs=10
exitcodes=0,2 ; 进程的预期退出代码列表,默认为0
stopsignal=QUIT ; 终止进程的信号,默认为TERM
redirect_stderr=true
stdout_logfile=/data/all_data/test/stdout.log
启动
supervisorctl reread
supervisorctl update
watch -n 1 "supervisorctl status"
重启
supervisorctl restart app
看日志
tail -f /data/all_data/test/stdout.log
报错
[2022-11-04 16:22:03 +0800] [38837] [INFO] Starting gunicorn 20.1.0
[2022-11-04 16:22:03 +0800] [38837] [ERROR] Connection in use: ('127.0.0.1', 5001)
[2022-11-04 16:22:03 +0800] [38837] [ERROR] Retrying in 1 second.
[2022-11-04 16:22:04 +0800] [38837] [ERROR] Connection in use: ('127.0.0.1', 5001)
手动杀死进程再重启即可
ss -nlp | grep 5001 # 查看端口占用
tcp LISTEN 0 128 127.0.0.1:5001 *:* users:(("gunicorn",pid=16485,fd=5),("gunicorn",pid=16483,fd=5),("gunicorn",pid=16481,fd=5),("gunicorn",pid=16471,fd=5))
kill -9 $(lsof -i tcp:5001 -t) # 杀死占用的进程
supervisorctl restart app # 重启
watch -n 1 "supervisorctl status" # 查看状态
修改配置文件
vim /etc/supervisor/conf.d/app.conf
添加一行
stopasgroup=true
停止并手动杀死进程
supervisorctl stop app
kill -9 $(lsof -i tcp:5001 -t)
更新配置并重启
supervisorctl reread
supervisorctl update
watch -n 1 "supervisorctl status"
再重启验证
supervisorctl restart app
看日志
tail -f /data/all_data/test/stdout.log
import time
import logging
import threading
import schedule
from flask import Flask
logger = logging.getLogger()
app = Flask(__name__)
@app.route('/')
def ping():
return 'pong\n'
def run():
def job():
logger.warning(str(time.time()))
schedule.every(3).seconds.do(job)
while True:
schedule.run_pending()
time.sleep(1)
threading.Thread(target=run, daemon=True).start()
if __name__ == '__main__':
app.run()