Как стать автором
Обновить

Глюки в библиотеках Python или нет?

Время на прочтение2 мин
Количество просмотров900
Писал я тут на днях web-спайдера на Python, задача, в общем-то, несложная, но нагрузки у нее серьезные, поэтому приходится запускать фактически пять спайдеров (в пяти потоках), кроме того, присутствуют несколько начальных условий, осложняющих дело… В общем, решение было интересным, выдалась возможность хорошенько полазить в потрохах стандартных питоньих либ socket, httplib и urllib2 (если интересно, могу и этот опыт описать).



О чем я хочу рассказать сейчас, это о том, к чему может привести пагубная привычка не следить за созданными объектами, прививаемая языками со сборкой мусора. Мониторя своего спайдера, я заметил, что в системе висит множество сокетов в состоянии CLOSE_WAIT. Причина этого в том, что сокеты уже закрыты со стороны сервера, но еще находятся в памяти. Т.е., грубо говоря, у сокета не был вызван метод close, а сам объект все еще где-то болтается в памяти.

Порывшись в urllib2, httplib и socket, я получил следующие сведения о механизме их работы:

  1. Для загрузки странички выполняется вызов urllib2.OpenDirector.open.
  2. Он вызывает метод urllib2.HTTPHandler.open, который в свою очередь вызывает urllib2.AbstractHTTPHandler.do_open
  3. В do_open создается объект h типа httplib.HTTPConnection для непосредственного выполнения задачи связи. Важный момент — этот объект пропадает при выходе из do_open!
  4. h порождает и открывает сокет, сохраняя его в своем атрибуте self.sock.
  5. h отсылает запрос серверу.
  6. do_open запрашивает у h ответ сервера и получает объект r типа httplib.HTTPResponse.
  7. Данный объект при создании на основе сокета h.sock создает файловый объект self.fp методом h.sock.makefile, который будет использоваться приложением для чтения данных. Опять важный момент — переданный в конструктор объект сокета нигде не сохраняется.
  8. do_open оборачивает полученный HTTPResponse в служебный объект и возвращает приложению.
  9. Приложение читает данные и закрывает HTTPResponse.


Таким образом, сам объект сокета (обертка над реальным сокетом), возможно, уже и не существует. По крайней мере, ссылок на него нигде не остается. Но сам сокет-то еще живет! Никто не вызывал у него close! Короче, пока на горячую руку мне в голову пришел только один вариант — после завершения чтения закрывать вручную сокет через пяток служебных ссылок «понятным» кодом следующего вида:
tf.fp._sock.fp._sock.close()

Где tf — ссылка, полученная из urllib2.open. Такие вот пироги :) Это кстати еще в 2.5; в 2.4 там еще есть пара багов похлеще. Буду рад любым подсказкам, как корректно победить такое поведение.
Теги:
Хабы:
Всего голосов 5: ↑4 и ↓1+3
Комментарии2

Публикации

Истории

Ближайшие события