Несколько процессов Python медленно - PullRequest
4 голосов
/ 01 октября 2011

У меня есть скрипт на python, который отключается и выполняет несколько запросов HTTP и urllib к различным доменам.

У нас огромное количество доменов для процессов, и мы должны сделать это как можно быстрее.Поскольку HTTP-запросы медленны (т. Е. Они могут истечь, из-за отсутствия веб-сайта в домене), я одновременно запускаю несколько сценариев, подающих их из списка доменов в базе данных.

Проблема, с которой я столкнулсяСм. в течение определенного периода времени (от нескольких часов до 24 часов), все сценарии начинают замедляться и ps -al показывает, что они спят.

Серверы очень мощные (8 ядер, 72 ГБ оперативной памяти, 6 ТБ)Raid 6 и т. Д. И 80 МБ (соединение 2: 1) и никогда не превышаются, т. Е. Free -m показывает

-/+ buffers/cache:      61157      11337
Swap:         4510        195       4315

Топ показывает 80-90% простоя

sar -d показывает в среднем 5,3% util

и, что более интересно, iptraf запускается со скоростью около 50-60 МБ / с и заканчивается со скоростью 8-10 МБ / с примерно через 4 часа.

В настоящее время я запускаю около 500 версий скриптана каждом сервере (2 сервера), и они оба показывают ту же проблему.

ps -al показывает, что большинство скриптов python спят, что я не понимаю, например, например:

0 S 0 28668  2987  0  80   0 - 71003 sk_wai pts/2 00:00:03 python
0 S 0 28669  2987  0  80   0 - 71619 inet_s pts/2 00:00:31 python
0 S 0 28670  2987  0  80   0 - 70947 sk_wai pts/2 00:00:07 python
0 S 0 28671  2987  0  80   0 - 71609 poll_s pts/2 00:00:29 python
0 S 0 28672  2987  0  80   0 - 71944 poll_s pts/2 00:00:31 python
0 S 0 28673  2987  0  80   0 - 71606 poll_s pts/2 00:00:26 python
0 S 0 28674  2987  0  80   0 - 71425 poll_s pts/2 00:00:20 python
0 S 0 28675  2987  0  80   0 - 70964 sk_wai pts/2 00:00:01 python
0 S 0 28676  2987  0  80   0 - 71205 inet_s pts/2 00:00:19 python
0 S 0 28677  2987  0  80   0 - 71610 inet_s pts/2 00:00:21 python
0 S 0 28678  2987  0  80   0 - 71491 inet_s pts/2 00:00:22 python

В сценарии, который получает эxecuted, поэтому я не могу понять, почему ps -al показывает, что большинство из них спят, и почему они должны работать медленнее и медленнее, делая меньше IP-запросов с течением времени, когда CPU, память, доступ к диску и пропускная способность доступны в изобилии.

Если бы кто-нибудь мог помочь, я был бы очень признателен.

РЕДАКТИРОВАТЬ:

Код является огромным, так как я использую исключения через него, чтобы перехватить диагностику о домене, то есть причины, по которым я не могу подключиться,При необходимости разместит код где-нибудь, но основные вызовы через HTTPLib и URLLib сразу же приводятся в примерах на python.

Дополнительная информация:

Оба

quota -u mysql quota-u root

возвращаться ни с чем

nlimit -n возвращается с 1024, имеют change limit.conf, чтобы разрешить mysql разрешать 16000 мягких и жестких соединений и может запускать более 2000 сценариев, поэтомудалеко, но все еще проблема.

НЕКОТОРЫЙ ПРОГРЕСС

Хорошо, поэтому я изменил все ограничения для пользователя, убедился, что все сокеты закрыты (они не были), и хотя все лучше,Я все еще замедляюсь, хотя и не так плохо.

Интересно, что я также заметил некоторую утечку памяти - скрипты используют все больше и больше памяти, чем дольше они работают, однако я не уверен, что является причиной этого.Я сохраняю выходные данные в строке, а затем печатаю их на терминале после каждой итерации. Я также очищаю строку в конце, но может ли когда-либо увеличиваться объем памяти до терминала, хранящего весь вывод?

Редактировать: Нет, похоже нет - запустил 30 скриптов без вывода на терминал и все той же утечки.Я не использую ничего умного (только строки, HTTPlib и URLLib) - интересно, есть ли какие-либо проблемы с соединителем Python mysql ...?

Ответы [ 4 ]

7 голосов
/ 01 октября 2011

Проверьте ulimit и quota для поля и пользователя, выполняющего сценарии./etc/security/limits.conf также может содержать ограничения на ресурсы, которые вы можете изменить.

ulimit -n покажет максимально допустимое количество дескрипторов открытых файлов.

  • Возможно, было превышено все открытые сокеты?
  • Является ли скриптзакрывать каждый сокет, когда это будет сделано?

Вы также можете проверить fd с помощью ls -l /proc/[PID]/fd/, где [PID] - идентификатор процесса одного из сценариев.

Нужно было бы увидеть какой-нибудь код, чтобы рассказать, что на самом деле происходит ..


Редактировать ( Импорт комментариев и дополнительных идей по устранению неполадок ):

Можете ли вы показать код, где ваши открывают и закрывают соединения?Когда просто запускаются несколько процессов сценария, они тоже начинают через некоторое время бездействовать?Или это происходит только тогда, когда одновременно работает более сотни человек?Есть ли один родительский процесс, который запускает все эти сценарии?

Если вы используете s = urllib2.urlopen(someURL), обязательно s.close(), когда вы закончите с ним.Python может часто закрывать для вас вещи (например, если вы делаете x = urllib2.urlopen(someURL).read()), но он оставляет это на вас , если вам приказано (например, присвоение переменной длявозвращаемое значение .urlopen()).Дважды проверьте открытие и закрытие вызовов urllib (или все коды ввода / вывода, чтобы быть в безопасности).Если каждый сценарий разработан так, чтобы иметь только 1 открытый сокет за раз, и ваш /proc/PID/fd показывает несколько активных / открытых сокетов на процесс сценария, то определенно существует проблема code , которую нужно исправить.

ulimit -n показывает 1024 дает предел открытых сокетов / fd , который может иметь пользователь mysql , вы можете изменить это с помощью ulimit -S -n [LIMIT_#] но сначала прочтите эту статью: Изменение дескриптора process.max-file-дескриптора с помощью 'ulimit -n' может привести к тому, что MySQL изменит значение table_open_cache .

Возможно, вам придется выйти из системы и вернуться обратно после нее.И / или добавьте его в /etc/bashrc (не забудьте source /etc/bashrc, если вы измените bashrc и не хотите выходить из системы).

Дисковое пространство Еще одна вещь, которую я обнаружил (трудный путь), может вызвать очень странные проблемы.У меня были процессы, действующие так, как будто они работают (не зомбированы), но не делали того, что ожидали, потому что у них были открытые дескрипторы для файла журнала в разделе с оставшимся дисковым пространством.

netstat -anpTee | grep -i mysql также покажет, еслиэти разъемы подключены / установлены / ожидают закрытия / ожидают по таймауту / и т. д.

watch -n 0.1 'netstat -anpTee | grep -i mysql', чтобы увидеть состояние открытия / закрытия / изменения сокетов / и т. д. в реальныхвремя в хорошем выводе таблицы (может понадобиться сначала export GREP_OPTIONS=, если у вас установлено что-то вроде --color=always).

lsof -u mysql или lsof -U также покажет вам открытые FD (вывод довольно многословный).


import urllib2
import socket

socket.settimeout(15) 
# or settimeout(0) for non-blocking:
#In non-blocking mode (blocking is the default), if a recv() call 
# doesn’t find any data, or if a send() call can’t
# immediately dispose of the data,
# a error exception is raised.

#......

try:
    s = urllib2.urlopen(some_url)
    # do stuff with s like s.read(), s.headers, etc..
except (HTTPError, etcError):
    # myLogger.exception("Error opening: %s!", some_url)
finally:
    try:
        s.close()
    # del s - although, I don't know if deleting s will help things any.
    except:
        pass

Некоторые справочные страницы и справочные ссылки:

2 голосов
/ 05 октября 2011

Решено! - с огромной помощью Чоуна - большое спасибо!

Замедление было связано с тем, что я не устанавливал таймаут сокета и, таким образом, в течение определенного периода времени роботы зависали, пытаясь прочитать данные, которых не было. Добавление простого

timeout = 5
socket.setdefaulttimeout(timeout)

решил (позор мне, но в свою защиту я все еще изучаю Python)

Утечка памяти до urllib и версии Python, которую я использую. Похоже, что после долгих поисков в Google возникает проблема с вложенными urlopens - много сообщений в Интернете об этом, когда вы решаете, как задать правильный вопрос Google.

Спасибо всем за помощь.

EDIT:

Что-то, что также помогло проблеме утечки памяти (хотя и не решило ее полностью), выполняло сборку мусора вручную:

import gc
gc.collect

Надеюсь, это поможет кому-то еще.

1 голос
/ 01 октября 2011

Другим системным ресурсом, который необходимо учитывать, являются временные порты /proc/sys/net/ipv4/ip_local_port_range (в Linux).Вместе с /proc/sys/net/ipv4/tcp_fin_timeout они ограничивают количество одновременных подключений.

От Тест производительности Python WSGI Servers :

Это в основном позволяет серверу открывать LOTSодновременные соединения.

echo “10152 65535″ > /proc/sys/net/ipv4/ip_local_port_range
sysctl -w fs.file-max=128000
sysctl -w net.ipv4.tcp_keepalive_time=300
sysctl -w net.core.somaxconn=250000
sysctl -w net.ipv4.tcp_max_syn_backlog=2500
sysctl -w net.core.netdev_max_backlog=2500
ulimit -n 10240
1 голос
/ 01 октября 2011

Вероятно, вам не хватает ресурсов системы.Угадайте: можете ли вы почувствовать ограничения пула сокетов, которые может обработать ваша система?Если да, вы можете увидеть улучшение производительности, если сможете быстрее / быстрее закрывать сокеты.

РЕДАКТИРОВАТЬ: в зависимости от усилий, которые вы хотите предпринять, вы можете реструктурировать свое приложение так, чтобы один процесс делал несколько запросов.Один сокет может быть повторно использован внутри одного и того же процесса, а также много разных ресурсов. Twisted очень подходит для этого типа программирования.

...