Uwsgi 与 gevent 对比线程
Uwsgi with gevent vs threads
首先,抱歉我的英语不好。
在我的项目中,我有很多 I/O 网络请求。主要数据存储在另一个项目中,访问方式为webAPI(JSON/XML),轮询。我们为每个新用户会话(获取有关用户的信息)使用此 APIs。有时,我们在等待回复时遇到问题。
我们使用nginx+uwsgi+django。如您所知,Django 是同步的(或阻塞的)。
我们使用多线程的 uwsgi 来解决网络 IO 等待的问题。
我决定阅读有关 gevent 的内容。我了解合作式和抢先式多任务处理之间的区别。我希望 gevent 比 uwsgi 线程更好地解决这个问题(网络 I/O 瓶颈)。但结果几乎相同。有时 gevent 较弱。
也许在某个地方我错了。请告诉我。
这里是 uwsgi 配置示例。
Gevent:
$ uwsgi --http :8001 --module ugtest.wsgi --gevent 40 --gevent-monkey-patch
线程:
$ uwsgi --http :8001 --module ugtest.wsgi --enable-threads --threads 40
控制器示例:
def simple_test_action(request):
# get data from API without parsing (only for simple I/O test)
data = _get_data_by_url(API_URL)
return JsonResponse(data, safe=False)
import httplib
from urlparse import urlparse
def _get_data_by_url(url):
u = urlparse(url)
if str(u.scheme).strip().lower() == 'https':
conn = httplib.HTTPSConnection(u.netloc)
else:
conn = httplib.HTTPConnection(u.netloc)
path_with_params = '%s?%s' % (u.path, u.query, )
conn.request("GET", path_with_params)
resp = conn.getresponse()
print resp.status, resp.reason
body = resp.read()
return body
测试(geventhttpclient):
def get_info(i):
url = URL('http://localhost:8001/simpletestaction/')
http = HTTPClient.from_url(url, concurrency=100, connection_timeout=60, network_timeout=60)
try:
response = http.get(url.request_uri)
s = response.status_code
body = response.read()
finally:
http.close()
dt_start = dt.now()
print 'Start: %s' % dt_start
threads = [gevent.spawn(get_info, i) for i in xrange(401)]
gevent.joinall(threads)
dt_end = dt.now()
print 'End: %s' % dt_end
print dt_end-dt_start
在这两种情况下,我都有相似的时间。 gevent/greenlets 和协作式多任务处理在类似问题(API 代理)中的优势是什么?
40的并发量还不是让gevent大放异彩的水平。 Gevent 是关于并发性而不是并行性(或每个请求的性能),因此具有这样的 "low" 级别的并发性并不是获得改进的好方法。
一般你会看到级别为千的gevent并发,而不是40 :)
阻塞I/Opython线程还不错(I/O期间释放了GIL),gevent的优势在于资源占用(有1000python线程将是矫枉过正)和删除需要考虑锁定和朋友。
显然,请记住,您的整个应用程序必须对 gevent 友好才能获得优势,而 django(默认情况下)需要进行一些调整(例如,数据库适配器必须更改为对 gevent 友好的内容)。
服务非阻塞不是关于性能,而是关于并发。如果 99% 的请求时间花在子请求上,你不能只优化这 99 %。但是当所有可用线程都忙于服务时,新客户端将被拒绝,尽管 99% 的线程时间都花在等待子请求完成上。非阻塞服务让您可以通过在 "handlers" 之间共享空闲时间来利用空闲时间,不再受可用线程数量的限制。因此,如果 99% 正在等待,那么另外 1% 是 CPU 绑定处理,因此在您最大化 CPU 之前,您可以同时拥有 100 倍以上的连接——而不需要 100 倍以上的线程,这可能太昂贵了(并且由于 Python 的 GIL 问题,您必须使用更昂贵的子流程)。
现在,正如罗伯托所说,您的代码必须 100% 非阻塞才能挽救空闲时间。但是,正如您从上面的百分比示例中看到的那样,仅当请求几乎完全受 IO 限制时,它才变得关键。如果是这种情况,您可能不需要 Django,至少对于您的应用程序的那部分而言是这样。
首先,抱歉我的英语不好。 在我的项目中,我有很多 I/O 网络请求。主要数据存储在另一个项目中,访问方式为webAPI(JSON/XML),轮询。我们为每个新用户会话(获取有关用户的信息)使用此 APIs。有时,我们在等待回复时遇到问题。 我们使用nginx+uwsgi+django。如您所知,Django 是同步的(或阻塞的)。 我们使用多线程的 uwsgi 来解决网络 IO 等待的问题。 我决定阅读有关 gevent 的内容。我了解合作式和抢先式多任务处理之间的区别。我希望 gevent 比 uwsgi 线程更好地解决这个问题(网络 I/O 瓶颈)。但结果几乎相同。有时 gevent 较弱。 也许在某个地方我错了。请告诉我。
这里是 uwsgi 配置示例。 Gevent:
$ uwsgi --http :8001 --module ugtest.wsgi --gevent 40 --gevent-monkey-patch
线程:
$ uwsgi --http :8001 --module ugtest.wsgi --enable-threads --threads 40
控制器示例:
def simple_test_action(request):
# get data from API without parsing (only for simple I/O test)
data = _get_data_by_url(API_URL)
return JsonResponse(data, safe=False)
import httplib
from urlparse import urlparse
def _get_data_by_url(url):
u = urlparse(url)
if str(u.scheme).strip().lower() == 'https':
conn = httplib.HTTPSConnection(u.netloc)
else:
conn = httplib.HTTPConnection(u.netloc)
path_with_params = '%s?%s' % (u.path, u.query, )
conn.request("GET", path_with_params)
resp = conn.getresponse()
print resp.status, resp.reason
body = resp.read()
return body
测试(geventhttpclient):
def get_info(i):
url = URL('http://localhost:8001/simpletestaction/')
http = HTTPClient.from_url(url, concurrency=100, connection_timeout=60, network_timeout=60)
try:
response = http.get(url.request_uri)
s = response.status_code
body = response.read()
finally:
http.close()
dt_start = dt.now()
print 'Start: %s' % dt_start
threads = [gevent.spawn(get_info, i) for i in xrange(401)]
gevent.joinall(threads)
dt_end = dt.now()
print 'End: %s' % dt_end
print dt_end-dt_start
在这两种情况下,我都有相似的时间。 gevent/greenlets 和协作式多任务处理在类似问题(API 代理)中的优势是什么?
40的并发量还不是让gevent大放异彩的水平。 Gevent 是关于并发性而不是并行性(或每个请求的性能),因此具有这样的 "low" 级别的并发性并不是获得改进的好方法。
一般你会看到级别为千的gevent并发,而不是40 :)
阻塞I/Opython线程还不错(I/O期间释放了GIL),gevent的优势在于资源占用(有1000python线程将是矫枉过正)和删除需要考虑锁定和朋友。
显然,请记住,您的整个应用程序必须对 gevent 友好才能获得优势,而 django(默认情况下)需要进行一些调整(例如,数据库适配器必须更改为对 gevent 友好的内容)。
服务非阻塞不是关于性能,而是关于并发。如果 99% 的请求时间花在子请求上,你不能只优化这 99 %。但是当所有可用线程都忙于服务时,新客户端将被拒绝,尽管 99% 的线程时间都花在等待子请求完成上。非阻塞服务让您可以通过在 "handlers" 之间共享空闲时间来利用空闲时间,不再受可用线程数量的限制。因此,如果 99% 正在等待,那么另外 1% 是 CPU 绑定处理,因此在您最大化 CPU 之前,您可以同时拥有 100 倍以上的连接——而不需要 100 倍以上的线程,这可能太昂贵了(并且由于 Python 的 GIL 问题,您必须使用更昂贵的子流程)。
现在,正如罗伯托所说,您的代码必须 100% 非阻塞才能挽救空闲时间。但是,正如您从上面的百分比示例中看到的那样,仅当请求几乎完全受 IO 限制时,它才变得关键。如果是这种情况,您可能不需要 Django,至少对于您的应用程序的那部分而言是这样。