Производительность 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').

Другие вопросы по тегам