2周刷完100道前端优质面试真题
flask中运用协程,经过gunicorn或uwsgi搭建web效劳时遇到的坑
我需求经过flask搭一个深度学习的接口,由于深度学习耗时很长,flask的http效劳是单向被动通讯,(客户端恳求,效劳器端响应)。客户端发来一个get恳求,http效劳在等候深度学习模型响应,假如数据量很大,衔接一定会超时。
需求就变成,我需求写一个flask接口,返回response的同时,还需求运转深度学习模型,这里运用了多线程,其中我还用协程调一个内部的查询api,部署时用docker+uwsgi。变成了docker+uwsgi+flask+threading+gevent。
问题就出在了gevent和uwsgi上,昨天坑了我一天并且我还没有找到缘由。
今天我就改成docker+gunicorn+flask+threading+gevent。胜利部署之后,我就开端排查发现是gevent+uwsgi出的问题。下面做了点实验考证我的想法,假如实验局部不想看,倡议拉到最后看结论局部。
PS . uwsgi 和 gunicorn 在windows装置根本不胜利,倡议运用docker或者linux系统,不然,努力半天就像什么都没干一样。
app = Flask(__name__)
# 模仿恳求
def get_url(num):
time.sleep(num)
print(f"cost:{num}s")
def gevent_get(msg):
print(msg)
s=perf_counter()
pool=Pool(5)
threads=[pool.spawn(get_url,num) for num in [1,2,3,2,1,2,3,4,4,5]]
gevent.joinall(threads
@app.route("/test", methods=["post","get"])
def test():
t = threading.Thread(target=gevent_get, args=("开端",))
t.start()
return jsonify({"code": 500,"msg": "success"})
if __name__ == '__main__':
app.run(debug=True)
复制代码
# test.py
import requests
URL="http://192.168.160.237:8000/test"
response=requests.get(URL).json()
print(response)
复制代码
# uwsgi.ini
[uwsgi]
# app是run.py里面的Flask对象
module = main:app
# 对外提供 http 效劳的端口
http = :8000
#指定工作进程
processes = 4
#主进程
master = true
#每个工作进程有2个线程
threads = 2
#指的后台启动 日志输出的中央
daemonize = uwsgi.log
#保管主进程的进程号
pidfile = uwsgi.pid
复制代码
docker build创立之后,经过uwsgi启动
uwsgi uwsgi.ini
假如按正常逻辑,输出应该是下面的结果
开端
[pid: 632|app: 0|req: 1/1] 172.17.0.1 () {32 vars in 386 bytes} [Tue Feb 15 05:28:25 2022] GET /test => generated 29 bytes in 4 msecs (HTTP/1.1 200) 2 headers in 71 bytes (3 switches on core 0)
sleep:1s
sleep:1s
sleep:2s
sleep:2s
sleep:3s
sleep:2s
sleep:3s
sleep:4s
sleep:4s
sleep:5s
破费时间8.007732099999885
复制代码
但是实践输出的结果,到“开端”就卡住了。也就是gevent没有用。
开端
[pid: 632|app: 0|req: 1/1] 172.17.0.1 () {32 vars in 386 bytes} [Tue Feb 15 05:28:25 2022] GET /test => generated 29 bytes in 4 msecs (HTTP/1.1 200) 2 headers in 71 bytes (3 switches on core 0)
复制代码
我昨天弄了一天也没处理,由于上面的代码是我今天想通之后简化的,开发代码逻辑要复杂更多。后面我就生气了,我就改成gunicorn。结果还是出问题了。
# 我就改成gunicorn.py
# 绑定的ip与端口
bind = "0.0.0.0:8000"
# 进程数
workers = 4
# 指定每个进程开启的线程数
# threads = 2
# 工作形式
worker_class = 'gthread'
# 处置恳求的工作线程数,运用指定数量的线程运转每个worker。为正整数,默许为1。
# worker_connections = 2000
# 设置pid文件的文件名,假如不设置将不会创立pid文件
pidfile = './gunicorn.pid'
# 要写入错误日志的文件目录。
errorlog = './log/gunicorn.error.log'
# 要写入的访问日志目录
accesslog = './log/gunicorn.access.log'
复制代码
正常输出应该是这样:
gunicorn -c gunicorn.py main:app
开端
sleep:1s
sleep:1s
sleep:2s
sleep:2s
sleep:3s
sleep:2s
sleep:3s
sleep:4s
sleep:4s
sleep:5s
复制代码
但这里通要还是会卡住
gunicorn -c gunicorn.py main:app
开端
复制代码
经过我多轮百度,翻看博客,发现gunicorn worker_class = 'gthread'有玄机。
我修正成worker_class = 'gevent'之后就一切正常了。默许的"sync"也是正常的
于是,我心血来潮想着是不是uwsgi也是和gevent有抵触。百度了一下,果真在uwsgi中运用协程要加一句话。
uwsgi uwsgi.ini --gevent 100 --gevent-early-monkey-patch
期间我还做了一下,gunicorn worker_class和uwsgi会不会对多线程有影响,结果是没有什么影响。
# main.py
import threading
from flask import Flask, request, jsonify
import time
app = Flask(__name__)
def get_url(num):
for i in range(num):
time.sleep(0.5)
print(i)
@app.route("/test", methods=["post","get"])
def test():
num=10
t = threading.Thread(target=get_url, args=(num,))
t.start()
return jsonify({"code": 500,"msg": "success"})
if __name__ == '__main__':
app.run(debug=True)
复制代码
补充
后续还会研讨一下,把上面的gevent交换成thread在做相同实验。
结论
在运用gevent+flask+uwsgi,配置启动
uwsgi uwsgi.ini --gevent 100 --gevent-early-monkey-patch
hi在运用gevent+flask+gunicorn,配置中形式worker_class = 'gevent'
gunicorn -c gunicorn.py main:app