我有一台服务器,用nginx,uwsgi和flask提供HTML服务。这是一个简单的webapp。刷新页面时,webapp会在其他地方进行一些API调用,然后处理从API返回的结果并生成HTML,然后返回。
现在,大部分时间都花在等待API响应上。因此,我正在使用gevent来进行API调用和猴子修补等,以使其全部并发。如果每次刷新页面都需要进行3次API调用,则无需等待第一次调用返回以启动第二次调用。您启动它们中的所有三个,等待它们的响应,然后生成要返回的HTML。
然而,一个单独的问题是页面从头到尾是否刷新本身是并发的。为了测试这个,我写了一个小的并发脚本来刷新我的服务器:
from gevent import monkey
monkey.patch_all()
import requests, datetime
import gevent
def call_func(n):
before = datetime.datetime.now()
print 'initiating call '+str(n),before
requests.get('http://examplemyserver.com')
after = datetime.datetime.now()
print 'finishing call '+str(n),after, after-before
gevent_list = [ gevent.spawn(call_func, n=n) for n in range(10) ]
gevent.joinall(gevent_list)
结果如下:
initiating call 0 2016-05-04 23:03:49.437540
initiating call 1 2016-05-04 23:03:49.446304
initiating call 2 2016-05-04 23:03:49.447911
initiating call 3 2016-05-04 23:03:49.450232
initiating call 4 2016-05-04 23:03:49.451774
initiating call 5 2016-05-04 23:03:49.453331
initiating call 6 2016-05-04 23:03:49.454759
initiating call 7 2016-05-04 23:03:49.456301
initiating call 8 2016-05-04 23:03:49.457663
initiating call 9 2016-05-04 23:03:49.458981
finishing call 0 2016-05-04 23:03:51.270078 0:00:01.832538
finishing call 6 2016-05-04 23:03:52.169842 0:00:02.715083
finishing call 3 2016-05-04 23:03:52.907892 0:00:03.457660
finishing call 1 2016-05-04 23:03:53.498008 0:00:04.051704
finishing call 8 2016-05-04 23:03:54.150188 0:00:04.692525
finishing call 4 2016-05-04 23:03:55.032089 0:00:05.580315
finishing call 7 2016-05-04 23:03:55.994570 0:00:06.538269
finishing call 2 2016-05-04 23:03:56.659146 0:00:07.211235
finishing call 9 2016-05-04 23:03:57.149156 0:00:07.690175
finishing call 5 2016-05-04 23:03:58.002210 0:00:08.548879
所以你看,十个页面刷新几乎同时启动,但是它们按顺序返回,相差约1秒。这表明,虽然内部外部API调用是同时完成的,但在给出HTTP响应之前,没有接受新的HTTP响应。
因此,考虑到大多数webapp时间都花在等待API响应上,我如何让整个过程并发?是否需要使用nginx进行更改?用uwsgi?带烧瓶?我不知道这些东西的架构。
感谢。
编辑1:
按照Rob的想法,我用'http://httpbin.org/delay/1'
替换了要调用的服务器。这是我获得的结果:
initiating call 0 2016-05-04 23:36:18.697813
initiating call 1 2016-05-04 23:36:18.706510
initiating call 2 2016-05-04 23:36:18.708645
initiating call 3 2016-05-04 23:36:18.711055
initiating call 4 2016-05-04 23:36:18.712679
initiating call 5 2016-05-04 23:36:18.714051
initiating call 6 2016-05-04 23:36:18.715471
initiating call 7 2016-05-04 23:36:18.717015
initiating call 8 2016-05-04 23:36:18.718412
initiating call 9 2016-05-04 23:36:18.720193
finishing call 0 2016-05-04 23:36:20.599483 0:00:01.901670
finishing call 2 2016-05-04 23:36:20.600829 0:00:01.892184
finishing call 8 2016-05-04 23:36:20.611292 0:00:01.892880
finishing call 5 2016-05-04 23:36:20.618842 0:00:01.904791
finishing call 7 2016-05-04 23:36:20.620065 0:00:01.903050
finishing call 6 2016-05-04 23:36:20.621344 0:00:01.905873
finishing call 1 2016-05-04 23:36:20.622407 0:00:01.915897
finishing call 4 2016-05-04 23:36:20.623392 0:00:01.910713
finishing call 9 2016-05-04 23:36:20.624236 0:00:01.904043
finishing call 3 2016-05-04 23:36:20.625075 0:00:01.914020
这是您对可以同时进行HTTP调用的服务器的期望。所以这表明我用于测试的上述代码是正确的,并且问题确实存在于服务器上。它不会同时进行HTTP调用。
EDIT2:我正在玩弄并检查最简单的烧瓶应用程序与我的服务器具有相同的行为,所以我将它作为我的“最小例子”
考虑以下服务器:
import time
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def hello():
time.sleep(2)
return ''
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80, debug=True)
现在通过获取前面的代码并将其指向http://localhost
来连接到它。结果如下:
initiating call 0 2016-05-04 23:49:12.629250
initiating call 1 2016-05-04 23:49:12.638554
initiating call 2 2016-05-04 23:49:12.643844
initiating call 3 2016-05-04 23:49:12.645630
initiating call 4 2016-05-04 23:49:12.647866
initiating call 5 2016-05-04 23:49:12.649332
initiating call 6 2016-05-04 23:49:12.650853
initiating call 7 2016-05-04 23:49:12.652448
initiating call 8 2016-05-04 23:49:12.653865
initiating call 9 2016-05-04 23:49:12.655348
finishing call 0 2016-05-04 23:49:14.671281 0:00:02.042031
finishing call 1 2016-05-04 23:49:16.673649 0:00:04.035095
finishing call 2 2016-05-04 23:49:18.676576 0:00:06.032732
finishing call 3 2016-05-04 23:49:20.679746 0:00:08.034116
finishing call 4 2016-05-04 23:49:22.681615 0:00:10.033749
finishing call 5 2016-05-04 23:49:24.683817 0:00:12.034485
finishing call 6 2016-05-04 23:49:26.686309 0:00:14.035456
finishing call 7 2016-05-04 23:49:28.688079 0:00:16.035631
finishing call 8 2016-05-04 23:49:30.691382 0:00:18.037517
finishing call 9 2016-05-04 23:49:32.696471 0:00:20.041123
所以基本上一个简单的烧瓶webapp不会同时进行HTTP调用。我怎么能改变这个?
答案 0 :(得分:1)
简短回答:启动uwsgi时使用--processes=N
和/或--threads=M
。
答案很长:您描述的堆栈涉及三个组件:nginx,它们是反向代理Web服务器的角色; uwsgi在应用服务器的角色;和烧瓶作为应用框架。数据流按此顺序发生,响应以反对顺序流动。
nginx本质上是多任务的,而且烧瓶本来就是单任务。 uwsgi可以配置为。默认情况下,uwsgi是单任务。
以下是我用来调查此问题的设置。我的操作系统是Ubuntu 14.04。
的/ etc / nginx的/位点可用/ MyApp的:
server {
listen 8100;
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/uwsgi.sock;
}
}
uwsgi命令行:
One of:
uwsgi -s /tmp/uwsgi.sock -w app:app
uwsgi -s /tmp/uwsgi.sock -w app:app --processes=4
uwsgi -s /tmp/uwsgi.sock -w app:app --threads=4
Followed by:
chmod 777 /tmp/uwsgi.sock
app.py:
import time
from flask import Flask, render_template_string
app = Flask(__name__)
@app.route('/')
@app.route('/<name>')
def hello(name='World'):
time.sleep(1)
return render_template_string('''
<html><body><p>
Hello, <b>{{ name }}</b>!
</p></body></html>''', name=name)
if __name__=="__main__":
app.run(debug=True)
客户:我使用了上面的gevent / requests客户端,替换了以下网址:http://localhost:8100/World
。
典型结果:
initiating call 0 2016-05-04 21:08:16.473978
initiating call 1 2016-05-04 21:08:16.485589
initiating call 2 2016-05-04 21:08:16.487258
initiating call 3 2016-05-04 21:08:16.490140
initiating call 4 2016-05-04 21:08:16.493158
initiating call 5 2016-05-04 21:08:16.494417
initiating call 6 2016-05-04 21:08:16.495620
initiating call 7 2016-05-04 21:08:16.497180
initiating call 8 2016-05-04 21:08:16.498413
initiating call 9 2016-05-04 21:08:16.499528
finishing call 3 2016-05-04 21:08:17.512949 0:00:01.022809
finishing call 6 2016-05-04 21:08:17.515944 0:00:01.020324
finishing call 7 2016-05-04 21:08:17.517655 0:00:01.020475
finishing call 4 2016-05-04 21:08:17.519555 0:00:01.026397
finishing call 2 2016-05-04 21:08:18.520249 0:00:02.032991
finishing call 0 2016-05-04 21:08:18.522902 0:00:02.048924
finishing call 5 2016-05-04 21:08:18.524902 0:00:02.030485
finishing call 1 2016-05-04 21:08:18.526682 0:00:02.041093
finishing call 9 2016-05-04 21:08:19.526909 0:00:03.027381
finishing call 8 2016-05-04 21:08:19.528926 0:00:03.030513
参考文献: