Писал я тут на днях web-спайдера на Python, задача, в общем-то, несложная, но нагрузки у нее серьезные, поэтому приходится запускать фактически пять спайдеров (в пяти потоках), кроме того, присутствуют несколько начальных условий, осложняющих дело… В общем, решение было интересным, выдалась возможность хорошенько полазить в потрохах стандартных питоньих либ
О чем я хочу рассказать сейчас, это о том, к чему может привести пагубная привычка не следить за созданными объектами, прививаемая языками со сборкой мусора. Мониторя своего спайдера, я заметил, что в системе висит множество сокетов в состоянии
Порывшись в
Таким образом, сам объект сокета (обертка над реальным сокетом), возможно, уже и не существует. По крайней мере, ссылок на него нигде не остается. Но сам сокет-то еще живет! Никто не вызывал у него close! Короче, пока на горячую руку мне в голову пришел только один вариант — после завершения чтения закрывать вручную сокет через пяток служебных ссылок «понятным» кодом следующего вида:
Где
socket
, httplib
и urllib2
(если интересно, могу и этот опыт описать).О чем я хочу рассказать сейчас, это о том, к чему может привести пагубная привычка не следить за созданными объектами, прививаемая языками со сборкой мусора. Мониторя своего спайдера, я заметил, что в системе висит множество сокетов в состоянии
CLOSE_WAIT
. Причина этого в том, что сокеты уже закрыты со стороны сервера, но еще находятся в памяти. Т.е., грубо говоря, у сокета не был вызван метод close
, а сам объект все еще где-то болтается в памяти.Порывшись в
urllib2
, httplib
и socket
, я получил следующие сведения о механизме их работы:- Для загрузки странички выполняется вызов
urllib2.OpenDirector.open
. - Он вызывает метод
urllib2.HTTPHandler.open
, который в свою очередь вызываетurllib2.AbstractHTTPHandler.do_open
- В
do_open
создается объектh
типаhttplib.HTTPConnection
для непосредственного выполнения задачи связи. Важный момент — этот объект пропадает при выходе изdo_open
! h
порождает и открывает сокет, сохраняя его в своем атрибутеself.sock
.h
отсылает запрос серверу.do_open
запрашивает уh
ответ сервера и получает объектr
типаhttplib.HTTPResponse
.- Данный объект при создании на основе сокета
h.sock
создает файловый объектself.fp
методомh.sock.makefile
, который будет использоваться приложением для чтения данных. Опять важный момент — переданный в конструктор объект сокета нигде не сохраняется. do_open
оборачивает полученныйHTTPResponse
в служебный объект и возвращает приложению.- Приложение читает данные и закрывает
HTTPResponse
.
Таким образом, сам объект сокета (обертка над реальным сокетом), возможно, уже и не существует. По крайней мере, ссылок на него нигде не остается. Но сам сокет-то еще живет! Никто не вызывал у него close! Короче, пока на горячую руку мне в голову пришел только один вариант — после завершения чтения закрывать вручную сокет через пяток служебных ссылок «понятным» кодом следующего вида:
tf.fp._sock.fp._sock.close()
Где
tf
— ссылка, полученная из urllib2.open. Такие вот пироги :) Это кстати еще в 2.5; в 2.4 там еще есть пара багов похлеще. Буду рад любым подсказкам, как корректно победить такое поведение.