【Django】项目中调用深度学习模型model.predict()(Django两种启动方式runserver和uwsgi的区别)
问题部署含有深度学习模型的Django项目的uWSGI、Nginx服务器的时候,所有模块都可以正常运行,也可以实现并发。唯独在处理需要调用深度学习模型的请求的时候无法成功运行,也不报错。一度更换了云主机,换成了有GPU的云主机,又掉进了显卡驱动的一堆坑中,在此记录一下。附上(腾讯CVM)GPU云主机显卡驱动。workers/processes一直busy但是没反应,卡着不动。
问题
部署含有深度学习模型的Django项目的uWSGI、Nginx服务器的时候,所有模块都可以正常运行,也可以实现并发。唯独在处理需要调用深度学习模型的请求的时候无法成功运行,也不报错,之后逐行debug发现总是卡在模型的model.predict()
这个函数中没法继续运行,在此记录一下。
另外曾一度怀疑是cpu跑不动了还更换了云主机,换成了有GPU的云主机,又掉进了显卡驱动的一堆坑中。附上(腾讯CVM)GPU云主机显卡驱动。
测试
- 直接runserver启动django,然后从http端口访问,可以成功调用深度学习模型;
- 通过uwsgi配置http并启动django,然后从http端口访问,无法调用深度学习模型,不报错;
- 通过nginx经socket访问uwsgi再启动django,然后从socket端口访问,无法调用深度学习模型,不报错;
鉴于其他模块都可以正常使用,直接怀疑是uWSGI的部署除了问题,检查uwsgi.ini,折腾了一阵子由于uwsgi配置项太多,折戟而返。于时借助其他的工具查找问题所在。
先用到了uwsgitop,看uwsgi负载情况,在uwsgi.ini设置了stats端口为127.0.0.1:8001
,故使用该端口即可查看性能。
$ pip install uwsgitop
$ uwsgitop 127.0.0.1:8001 #实际上用curl 127.0.0.1:8001可以看到json格式数据,只是不直观
可以看到正常情况下负载如下
各参数含义如下
当我调用深度学习模型的时候,worker总是处于busy状态,但是其他参数跟cheap的worker一样,一直卡着不动了。
于是使用lsof查看运行情况
(root)$ lsof -nPp pid 看卡住的pid
调用深度模型时的lsof -nPp 2256
:
不调用深度模型时的lsof -nPp 32735
:
结合netstat -anpt
查看端口使用情况发现9001端口和45728端口建立链接总是处于CLOSE_WAIT状态,而在没有调用深度模型的时候就不会出现这种情况。实际上这是因为调用深度模型的时候,程序执行不下去,nginx没法得到结果所以只能保持close_wait状态。
只找到了结果,没找到原因。
继续使用strace查看运行情况
$ strace -ttp pid #看卡住的pid
调用深度模型时strace:
参考Linux中的线程同步机制(一) — Futex
这里futex操作正在等待另一个线程释放一个锁
于时索性不启动线程,且之启动一个进程,在uwsgi.ini里面再次配置如下
$ master = false #不启动主进程
$ processes = 1 #最大进程数为1
$ cheaper = 0 #最小进程数为0
!!!发现成功运行深度学习模型。
解决方法
总结一下:之所以只能用一个进程且主线程不能开启,是因为深度学习模型调用的时候都需要一段时间,如果有多个进程势必会争夺资源,即使只有一个请求的时候,可能是因为主进程和子进程同时在运行导致没法调用深度学习模型。另外除了上面的三个限制,其他限制还有下面两条:
- 不能将项目中存放且要用的图片和视频作为静态资源被收集,收集的静态资源一般都是css,html,js这些;
- 不能开启thunder-lock;
- 建议关闭stats,因为主进程不开启的话就没办法使用stats,再打开也是多次一举
另外似乎因为没有主线程的关系(只有一个进程),现在要杀掉一个uwsgi,如果不能通过uwsgi --stop uwsgi.pid 来杀uwsgi而是通过删除占用端口的pid,则需要删除很多个pid对应的程序,比之前麻烦很多,用这个fuser -k (uwsgi的socket号)/tcp
方便一些,更快捷的方法是killall -9 uwsgi
目前为止,通过uwsgi依然只实现了单进程,没法并发处理请求,更别提并行。为了进一步提高并发并行性能,只开1个processes并且尝试打开多线程,
$ enable-threads=true
$ threads = 4
再次测试,由于线程之间资源共享,此时多个线程可以并发(因为GIL所以不能并行),服务器也相对稳定,暂时可以满足需求,但毕竟只有一个进程,要进一步提高并发能力的话,关键是要解决多进程调度的问题。
这里考虑两个可能造成只能开启单线程的原因:
- 使用了
from django.utils.module_loading import autodiscover_modules
的方式预加载模型,可能导致模型变成了全局变量,在多进程争夺资源的时候引起阻塞。 - 本身就需要使用multiprocessing来支持多进程的调度
这个问题需要解决才能实现Nginx+uWSGI实现高并发,充分利用多核计算资源。这里就涉及到了runserver和uwsgi两种启动方式的区别。
Django两种启动方式runserver和uwsgi的区别
Django原生为单线程
Django server 默认多线程
uwsgi启动时,可以单进程+单线程;单进程+多线程;多进程+单线程;多进程+多线程。后三个都可以并发,能否并行还得看操作系统和cpu的情况。
详细内容可以参考https://blog.csdn.net/cpxsxn/article/details/106102441
更多推荐
所有评论(0)