FutureSession: соединения сокетов не закрыты

Я работаю над программой PyQt, которая в основном собирает данные из Интернета. В этом примере я пытаюсь получить данные с веб-страницы RSS.

Предположим, что self.feed — это страница RSS, содержащая все статьи, а «запись» — это статья. «entry.url» — это исходная страница статьи на веб-сайте.

from requests_futures.sessions import FuturesSession

self.session_pages = FuturesSession(max_workers=20)
for entry in self.feed.entries:
    future = self.session_pages.get(entry.url, timeout=10)
    future.add_done_callback(my_call_back)

Я в основном так и делаю. Он встроен в поток PyQt, и я одновременно запускаю несколько потоков, но я думаю, что проблема не в PyQt.

Моя проблема в том, что я думаю, что фьючерсы не закрывают соединение, даже когда они сделаны. Проверяю так:

lsof -i | grep "python" | wc -l

losf -i дает открытые файлы, участвующие в соединении. Остальная часть команды предназначена для подсчета количества открытых файлов. Это число не перестает расти (что-то вроде 900), и тогда я получаю следующую ошибку:

(python:28285): GLib-ERROR **: Creating pipes for GWakeup: Too many open files
[1]    28285 trace trap (core dumped)  python gui.py

Я думаю, что проблема связана с фьючерсами, но на самом деле я не уверен.

Я пробовал что-то вроде:

self.session_pages.shutdown()

в конце темы, но это не сработало.

Есть ли у вас какие-либо идеи ?


person JPFrancoia    schedule 19.04.2015    source источник
comment
если вы помните, какие результаты вы получили после запуска lsof -i | grep "python" | wc -l?   -  person user3661992    schedule 19.08.2020


Ответы (2)


Хорошо, ты был прав @jm_____. вызов get() — это просто вызов request.get. Поэтому я использовал ответ отсюда:

https://stackoverflow.com/questions/10115126/python-requests-close-http-connection

И, более конкретно:

future = self.session_pages.get(url, timeout=20, headers={'Connection':'close'})

И теперь lsof указывает нормальные числа. Спасибо.

person JPFrancoia    schedule 19.04.2015
comment
Потрясающий. Рад, что смог заставить тебя двигаться дальше. - person jmunsch; 20.04.2015

Я не вижу FutureSession в concurrent.futures Python? Здесь я делаю некоторые предположения.

Если обратный вызов не уникален для каждого self.session_page.get(...), я думаю, что строка future.add_done_callback(my_call_back) может создавать новые и перезаписывать идентификатор объекта обратного вызова или может быть неправильным?

Это из единственного места, где я мог найти ссылку на FutureSession в контексте того, что вы с использованием:

from pprint import pprint
from requests_futures.sessions import FuturesSession

session = FuturesSession()

def bg_cb(sess, resp):
    # parse the json storing the result on the response object
    resp.data = resp.json()

future = session.get('http://httpbin.org/get', background_callback=bg_cb)
# do some other stuff, send some more requests while this one works
response = future.result()
print('response status {0}'.format(response.status_code))
# data will have been attached to the response object in the background
pprint(response.data)

попробуйте установить background_callback

Обновлять:

Я бы попробовал использовать self.session_pages.request вместо self.session_pages.get, так как FutureSession состоит из исполнителя пула потоков и requests.Sessions

Да дело в этом:

(Pdb) inspect.getmro(FuturesSession)
(<class '__main__.FuturesSession'>, <class 'requests.sessions.Session'>, <class 'requests.sessions.SessionRedirectMixin'>, <class 'object'>)
(Pdb) vars()
{'DEFAULT_POOLSIZE': 10, '__return__': None, '__spec__': None, 'inspect': <module 'inspect' from '/usr/lib/python3.4/inspect.py'>, '__file__': 'requestsfutures.py', 'FuturesSession': <class '__main__.FuturesSession'>, 'HTTPAdapter': <class 'requests.adapters.HTTPAdapter'>, 'ThreadPoolExecutor': <class 'concurrent.futures.thread.ThreadPoolExecutor'>, 'Session': <class 'requests.sessions.Session'>, '__name__': '__main__', '__cached__': None, '__doc__': "\nrequests_futures\n~~~~~~~~~~~~~~~~\n\nThis module provides a small add-on for the requests http library. It makes use\nof python 3.3's concurrent.futures or the futures backport for previous\nreleases of python.\n\n    from requests_futures import FuturesSession\n\n    session = FuturesSession()\n    # request is run in the background\n    future = session.get('http://httpbin.org/get')\n    # ... do other stuff ...\n    # wait for the request to complete, if it hasn't already\n    response = future.result()\n    print('response status: {0}'.format(response.status_code))\n    print(response.content)\n\n", 'pdb': <module 'pdb' from '/usr/lib/python3.4/pdb.py'>, '__loader__': <_frozen_importlib.SourceFileLoader object at 0x7f6d84194470>, '__builtins__': <module 'builtins' (built-in)>, '__package__': None}
(Pdb) vars().keys()
dict_keys(['DEFAULT_POOLSIZE', '__return__', '__spec__', 'inspect', '__file__', 'FuturesSession', 'HTTPAdapter', 'ThreadPoolExecutor', 'Session', '__name__', '__cached__', '__doc__', 'pdb', '__loader__', '__builtins__', '__package__'])
(Pdb) vars()['FuturesSession']
<class '__main__.FuturesSession'>
(Pdb) vars()['FuturesSession'].get
<function Session.get at 0x7f6d80c07488>
(Pdb) vars()['Session'].get
<function Session.get at 0x7f6d80c07488>
person jmunsch    schedule 19.04.2015
comment
Да, ты прав, мой плохой. Я отредактировал свой вопрос и добавил файл import. Но на самом деле FuturesSession является оберткой для ThreadPoolExecutor (думаю, это класс, я не помню), а futures FutureSession в основном concurrent.futures. - person JPFrancoia; 19.04.2015
comment
обновил мой ответ, посмотрев, как это работает. Я думаю, что при вызове get вызов использует обычный метод requests get и вообще не вызывает какой-либо переопределенный обратный вызов/метод. - person jmunsch; 19.04.2015