Писал я тут на днях 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 там еще есть пара багов похлеще. Буду рад любым подсказкам, как корректно победить такое поведение.