1、测试环境配置如下:
虚拟机:Ubuntu16.04 4G内存 双核处理器
运行环境python+flask+gunicorn+nginx
flask 、nginx和 gunicorn 安装方法都非常简单粗暴:sudo apt-get install 就好
附上一个安装教程点击打开链接
这里我们为了使用一个域名来代替访问默认的127.0.0.1:8080,将配置nginx如下:
在/etc/nginx/conf.d 下新建文件flask_nginx.conf
添加如下内容:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
server {
listen 80;
server_name www.flasktest.com;
location / {
proxy_pass http://127.0.0.1:8080;
access_log /home/api_access.log;
proxy_read_timeout 300;
}
}
在/etc/hosts文件中新增一行 127.0.0.1 www.flasktest.com
通过如上配置,我们就可以通过访问www.flasktest.com域名来访问127.0.0.1:8080端口下的内容啦~
2、具体代码:
from flask import Flask,request,jsonify
from flask_restful import reqparse, abort, Api, Resource
import time
app = Flask(__name__)
api = Api(app)
# 这是第一个接口
class task_1(Resource):
def get(self): #这里的get函数,指定处理get请求
parser = reqparse.RequestParser()
parser.add_argument("url", type=str, location="args", required=False)
parser.add_argument("type", type=int, location="args", required=False)
parser.add_argument("id", type=str, location="args", required=False)
params = parser.parse_args(strict=False)
print(params)
if params['action'] == 'fresh':
#run something
return 'vuln.cn demo 1'
else:
#run something
return 'hello vuln.cn'
# 这是第二个接口
class task_2(Resource):
#global num
#num = 1000
def get(self):
#global num
#num -= 1
s = time.time()
count = 0
for i in range(0,1000000): //实现cpu计算
count += i
#time.sleep(2) //实现I/O
parser = reqparse.RequestParser()
parser.add_argument("id", type=str, location="args", required=False)
params = parser.parse_args(strict=False)
#run something
e = time.time()
run_time = e - s
res = {}
res['time'] = run_time
#res['num'] = num
return res
#路由地址分别对应处理的类
api.add_resource(task_1, '/demo1/') #实现接口:http://127.0.0.1:8080/demo1/?url=xxx&type=xxx&id=xxx
api.add_resource(task_2, '/demo2/') #实现接口:http://127.0.0.1:8080/demo2/?id=xxx
if __name__ == '__main__':
app.run(debug=True)
这里我们只使用了接口二:同时因为我们之前在nginx下的配置,我们也可以通过访问http://www.flasktest.com/demo2/?id=xxx来进行实验。
程序中for循环是为了获得cpu计算所消耗的时间,而sleep则是为了获得I/O所消耗的时间。
3、使用gunicorn在服务器端启动多进/线程
启动多个进程:
gunicorn -w 40 -t 60000 flask_rest_test:app -b 0.0.0.0:8080
参数解释:-w 指定启动的进程数
-t 指定超时时间 默认30s
flask_rest_test:app 前半部分为文件名称,后半部分为app的名字,对应代码中app = Flask(__name__)
-b 指定IP和端口号执行 0.0.0.0 表示接受所有IP访问
启动多个线程:
gunicorn -w 1 --threads 40 -t 60000 flask_rest_test:app -b 0.0.0.0:8080
参数解释:与上面相比多出了一个threads参数
--threads 指定启动的线程数
4、使用CURL发起多并发请求
脚本如下:新建formuti.sh,内容如下:
stime=$(date +%s);
#echo "${stime}";
for((i=0;i<700;i++));
do
{
curl -v http://www.flasktest.com/demo2/?id=2222;
}&
done
wait
etime=$(date +%s);
((run_time = etime - stime));
#((run_time = run_time * 1000))
echo "${run_time}"s;
#echo $num;
这段代码表示同时发起700个并发请求,程序中&表示 去并发提交函数中的内容,而不是执行完一个,再去循环执行下一个。
wait表示前面的程序全部执行完,才会继续执行下面的命令。
5、实验结果
0-1000000加法:
sleep2秒的时间消耗:
6、总结
对于cpu计算来说,越高并发并且开启的进/线程越多,同等情况相比较而言,线程执行时间越短。其他情况则进程执行时间较短。对于cpu计算来说,并发数和开启进/线程都较低的情况,选用多进程为更好的选择,若并发数目过高和开启的进/线程较多的情况,易选用多线程。
对于I/O来说,并发数目和开启的进/线程较少的情况下,运行时间相差无几。当并发数远远大于开启的进/线程时,进程运行时间较短,但是存在访问被丢弃。其他情况则线程运行时间较短。另外开启进程数目过多,非常容易卡死。所以对于I/O来说,多线程为一个更好的选择。