Производительность Python httplib (API Discogs)
Я написал небольшую прогу, которая использует API Discogs с python, но она настолько чертовски медленная, что ее нельзя использовать в реальных веб-приложениях. Вот код Python и результаты профиля Python (публикуются только споты, занимающие много времени):
# -*- coding: utf-8 -*-
import profile
import discogs_client as discogs
def main():
discogs.user_agent = 'Mozilla/5.0'
#dump released albums into the file. You could also print it to the console
f=open('DiscogsTestResult.txt', 'w+')
#Use another band if you like,
#but if you decide to take "beatles" you will wait an hour! (cause of the num of releases)
artist = discogs.Artist('Faust')
print >> f, artist
print >> f," "
artistReleases = artist.releases
for r in artistReleases:
print >> f, r.data
print >> f,"---------------------------------------------"
print 'Performance Analysis of Discogs API'
print '=' * 80
profile.run('print main(); print')
и вот результат профиля питонов:
Performance Analysis of Discogs API
================================================================================
82807 function calls (282219 primitive calls) in 177.544 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
188 121.013 0.644 121.013 0.644 :0(connect)
206 52.080 0.253 52.080 0.253 :0(recv)
1 0.036 0.036 177.494 177.494 <string>:1(<module>)
188 0.013 0.000 175.234 0.932 adapters.py:261(send)
376 0.005 0.000 0.083 0.000 adapters.py:94(init_poolmanager)
188 0.008 0.000 176.569 0.939 api.py:17(request)
188 0.007 0.000 176.577 0.939 api.py:47(get)
188 0.015 0.000 173.922 0.925 connectionpool.py:268(_make_request)
188 0.015 0.000 174.034 0.926 connectionpool.py:332(urlopen)
1 0.496 0.496 177.457 177.457 discogsTestFullDump.py:6(main)
564 0.009 0.000 176.613 0.313 discogs_client.py:66(_response)
188 0.012 0.000 176.955 0.941 discogs_client.py:83(data)
188 0.011 0.000 51.759 0.275 httplib.py:363(_read_status)
188 0.017 0.000 52.520 0.279 httplib.py:400(begin)
188 0.003 0.000 121.198 0.645 httplib.py:754(connect)
188 0.007 0.000 121.270 0.645 httplib.py:772(send)
188 0.005 0.000 121.276 0.645 httplib.py:799(_send_output)
188 0.003 0.000 121.279 0.645 httplib.py:941(endheaders)
188 0.003 0.000 121.348 0.645 httplib.py:956(request)
188 0.016 0.000 121.345 0.645 httplib.py:977(_send_request)
188 0.009 0.000 52.541 0.279 httplib.py:994(getresponse)
1 0.000 0.000 177.544 177.544 profile:0(print main(); print)
188 0.032 0.000 176.322 0.938 sessions.py:225(request)
188 0.030 0.000 175.513 0.934 sessions.py:408(send)
752 0.015 0.000 121.088 0.161 socket.py:223(meth)
2256 0.224 0.000 52.127 0.023 socket.py:406(readline)
188 0.009 0.000 121.195 0.645 socket.py:537(create_connection)
Кто-нибудь есть идеи, как ускорить это. Я надеюсь, что с некоторыми изменениями в discogs_client.py это будет быстрее. Может быть, перейти с httplib на что-то еще, или что-то еще. Или, может быть, быстрее использовать другой протокол вместо http?
(Источник discogs_client.py доступен здесь: " https://github.com/discogs/discogs_client/blob/master/discogs_client.py")
Если у кого-то есть идеи, пожалуйста, ответьте, многие люди выиграют от этого.
С уважением, Даниэль
2 ответа
ОБНОВЛЕНИЕ: Из документации Discogs: Requests are throttled by the server to one per second per IP address. Your application should (but doesnt have to) take this into account and throttle requests locally, too.
Узкое место, по-видимому, находится на стороне сервера (discogs), получая отдельные выпуски. Вы ничего не можете с этим поделать, кроме как дать им деньги за более быстрые серверы.
Мое предложение состоит в том, чтобы кэшировать результаты, возможно, это единственное, что поможет. Перепишите discogs.APIBase._response, следующим образом:
def _response(self):
if not self._cached_response:
self._cached_response=self._load_response_from_disk()
if not self._cached_response:
if not self._check_user_agent():
raise UserAgentError("Invalid or no User-Agent set.")
self._cached_response = requests.get(self._uri, params=self._params, headers=self._headers)
self._save_response_to_disk()
return self._cached_response
Альтернативный подход - записать запросы в журнал и сказать "мы не знаем, попробуйте позже", затем в другом процессе прочитайте журнал, загрузите данные, сохраните их в базе данных. Затем, когда они вернутся позже, запрошенные данные будут там готовы.
Вам нужно было бы написать _load_response_from_disk() и _save_response_to_disk() самостоятельно - сохраненные данные должны иметь _uri, _params, and _headers
в качестве ключа, и должен включать временную метку с данными. Если данные слишком старые (в данных обстоятельствах я бы предложил в порядке месяцев - я понятия не имею, является ли нумерация постоянной - я бы предположил, что пробовал дни - недели вначале) или не найдена, верните None. Хранилище должно обрабатывать одновременный доступ и быстрые индексы - возможно, базу данных.
Попробуйте это: вместо использования print >> для записи в файл используйте f.write('hello\n').