最近做了一个检测数据的项目.总的来说,很简单,就是一个不停运行的脚本,监控数据源,有新数据时就检测,并及时将检测结果在前端展示出来.虽然说的很简单,但是在真正的实施过程中遇到了很多的坑,所以,这里总结下:
1.supersivor使用的问题:
(1)检测脚本是用supervisor监控的,挂掉了就会重启,这很方便.但因为脚本里面我单独调用了django 的orm,并且通过cx_Oracle连接oracle数据库,这里出现了三个问题:
a.调用dajngo 的orm单独运行没问题,通过服务启动报错:找不到应用.
如上,在命令行运行此脚本时,一切正常,但是通过supervisor启动时,死活报错,找不到应用.
解决办法:在脚本添加应用的路径,如:
import os,django import sys sys.path.append('/.../.../.../')
b.通过服务启动,cx_Oracle连接不到数据库,或者找不到64位的lib包,或者连接上了,但是查找一些特殊字符时报错.os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testapp.settings')django.setup()
我的oracle 11g数据库是装在本地,服务器上装的是12.2的客户端.同样的,脚本正常可以运行,但是通过服务启动时就报错.
解决办法:将客户端的的路径写入脚本里面,如下:
os.environ['ORACLE_HOME'] = '/opt/oracle/instantclient_12_2' os.environ['TNS_ADMIN'] = '/opt/oracle/instantclient_12_2/network/admin' os.environ['NLS_LANG'] = 'AMERICAN_AMERICA.AL32UTF8' os.environ['PATH'] = '/opt/oracle/instantclient_12_2:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games' os.environ['LD_LIBRARY_PATH'] = '/opt/oracle/instantclient_12_2:$LD_LIBRARY_PATH'
里面的路径是客户端的路径,可根据自己实际情况修改.
还要在命令行运行:
export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2:$LD_LIBRARY_PATH
sudo sh -c 'echo /opt/oracle/instantclient_12_2 > /etc/ld.so.conf.d/oracle-instantclient.conf'
sudo ldconfig
c.oracle 连接池的问题.
为避免短连接开销过大,打算利用oracle的连接池,如下:
oraclepool = cx_Oracle.SessionPool(user='#', password='#', dsn='#', min=1, max=10, increment=1)dsn是'ip:port/database'的样式,后面参数表示最大10个连接,每次自增.
使用时,先将连接池传进函数,处理完之后要将游标关闭:
conn = oraclepool.acquire() cursor = conn.cursor()
cursor.close()(2).通过supervisor监控uwsgi + nginx部署django 的问题.
supervisor可以在你的程序断掉的时候重启,但是有个问题,就是它不能判断你的程序是否成功启动,如果你的程序僵死在哪里呢?
a.uwsgi 端口占用的问题.
一开始supervisor的配置命令是直接启动uwsgi,比如: uwsgi --ini /.../.../...ini,这样很容易出现个问题,就是端口占用.
比如,我uwsgi的ini绑定的是80端口,但一开始80端口就被占用了,你supervisor就会判断启动不了,一直重启,但怎么都起不来.
解决办法:绕一下,不直接启动uwsgi,而是supervisor 启动一个监控脚本,这个脚本监控uwsgi的进程,当进程里面没有uwsgi时,都会杀端口,在通过os.system 启动uwsgi
杀80端口的命令是: sudo fuser -k 80/tcp
b.supervisor 重启后uwsgi cpu占满的问题.
supervisor重启时,uwsgi的master进程会僵死掉的,但是它的slave进程不会,就一直在找master进程,但一直找不到,就会占满cpu了.
一开始,是在ini文件里面 添加:
die-on-term = true但是没效果,最后研究发现,uwsgi 在进程里面正常的状态是'S', 但是僵死后会变成'R'或者'D',那就很好办了.在上面说的监控脚本里面添加判断:一个是没进程时就将uwsgi拉起来,有进程时就判断该进程的状态,若状态不对,就杀掉uwsgi 进程,重新拉起来
暴力杀掉uwsgi命令是: killall -9 uwsgi
2.程序运行cpu 过100的问题.
脚本设计的是在一个while循环里面嵌套另一个while循环,还涉及到对mysql的一些操作,所以程序运行时python 和 mysql 的cpu 以下就过100 了,很影响前端的展示,对服务器性能也不好.
解决办法:都知道cpu 在1S内是可以做很多事的,所以为了避免cpu占用的问题,有时候,就算休眠0.1s也是很有用的.因此,在一些程序的节点上,增加了个time.sleep(0.1),降低了cpu的占用.
3.前端页面展示查询慢的问题.
客户的需求是要实时将已检测的结果去重复后展示在最前面.一开始是直接一张表group by 和 order by 加上一些字段索引是可以实现的.但到了后期, 上亿的数据量,并且重复的数据占比很大,这时候就慢死了.
中间是分表,将已检出的去掉重复放在一张表,未检出的去重复放一张表,直接取第一张表就行了.但之后又要求将未检出的要拼接在已检出的后面,因为这些未检出的可能还有用.好吧,union all 加上limit.但有出现个问题,两张表太大了,千万级了,union all 也很慢.最后,换了种方法弄好:就是判断三种情况,假设num 为可以从第一张表里面拿取的数据量,当num > 10(我一页为10条), 就不从第二张表拿了,当0 < num < 10时,就union 第二张表,拿够10条,当 num = 0 时,就直接从第二张表拿了,这时候只需要判断是第几页,然后减去拿第一张表的页数就可以了.
查询慢还做了些其他的改变,比如where, group by,order by 后面的字段添加索引,尽量不要用外键,避免全表扫描,尽量用主键索引,等等.