Вручную остановить процессы, запущенные mod_wsgi, и контролировать, сколько процессов выполняется

Я знаю, что не рекомендуется запускать приложение "Бутылка или python myapp.py --port=80 при python myapp.py --port=80 с помощью python myapp.py --port=80 потому что это только сервер разработки.

Я думаю, что не рекомендуется также запускать его с python myapp.py --port=5000 и связывать его с Apache с помощью: RewriteEngine On, RewriteRule/(.*) http://localhost:5000/$1 [P,L] (или я ошибаюсь?), потому что WSGI является предпочтительным.

Поэтому я в настоящее время настраиваю Python app <-> mod_wsgi <-> Apache (без оружия или другого инструмента, чтобы все было просто).

Вопрос: при использовании WSGI я знаю это Apache и mod_wsgi, которые автоматически запускают/останавливают достаточно процессов, запускающих myapp.py когда будут поступать запросы, но:

  1. как я могу вручную остановить эти процессы?
  2. в более общем плане, есть ли способ контролировать их/знать, сколько процессов, запущенных mod_wsgi, в настоящее время все еще работает? (одна из причин, среди прочего, заключается в том, чтобы проверить, прекращаются ли процессы после запроса или если они будут работать)

Пример:

  • Я внес некоторые изменения в myapp.py, и я хочу перезапустить все запущенные им процессы, которые были запущены mod_wsgi (Примечание: я знаю, что mod_wsgi может просматривать изменения в исходном коде и перезапускать, но это работает только при внесенных изменениях в файле.wsgi, а не в файле.py. Я уже прочитал, что touch myapp.wsgi может быть для вас решением, но в целом я бы хотел остановить и перезапустить вручную)

  • Я хочу временно остановить все приложение myapp.py (все его экземпляры)

Я не хочу использовать service apache2 stop для этого, потому что я также запускаю другие сайты с Apache, а не только этот (у меня есть несколько VirtualHosts). По той же причине (я запускаю другие веб-сайты с Apache, и некоторые клиенты могут одновременно загружать 1 ГБ файл), я не хочу service apache2 restart, что повлияет на все сайты, использующие Apache.

Я ищу более чистый способ, чем kill pid или SIGTERM и т.д. (Потому что я читал, что в этом случае не рекомендуется использовать сигналы).

Примечание. Я уже прочитал. Как сделать изящное закрытие приложения из mod_wsgi, это помогло, но здесь это дополнительные вопросы, а не дубликаты.


Моя текущая установка Python + Apache + mod_wsgi:

  • Монтаж:

    apt-get install libapache2-mod-wsgi
    a2enmod wsgi      # might be done automatically by previous line, but just to be sure
    
  • Конфигурация Apache (источник: Bottle doc):

    <VirtualHost *:80>
      ServerName example.com
      WSGIDaemonProcess yourapp user=www-data group=www-data processes=5 threads=5
      WSGIScriptAlias / /home/www/wsgi_test/app.wsgi
      <Directory /home/www/wsgi_test>
        WSGIProcessGroup yourapp
        WSGIApplicationGroup %{GLOBAL}
        Require all granted
      </Directory>
    </VirtualHost>
    

    Там должно быть до 5 процессов, верно? Как указывалось ранее в вопросе, как узнать, сколько работает, как их остановить?

  • /home/www/wsgi_test/app.wsgi (источник: Bottle doc)

    import os
    from bottle import route, template, default_app
    
    os.chdir(os.path.dirname(__file__))
    
    @route('/hello/<name>')
    def index(name):
        return template('<b>Hello {{name}}</b>!', name=name)
    
    application = default_app()
    

Ответ 1

Взяв частично из этого вопроса, добавьте display-name в WSGIDaemonProcess чтобы вы могли захватить их, используя команду:

ps aux | grep modwsgi

Добавьте это в свою конфигурацию:

Define GROUPNAME modwsgi
WSGIDaemonProcess yourapp user=www-data group=www-data processes=5 threads=5 display-name=%{GROUPNAME}

Обновить

Есть несколько причин, по которым ps не даст вам DaemonProcess display-name.
Как показано в документах:

display-name = value Определяет другое имя для демонстрации процесса daemon при использовании команды ps для перечисления процессов. Если значение% {GROUP}, то имя будет (wsgi: group), где группа заменяется именем группы процессов демона.

Обратите внимание, что может отображаться столько символов заданного значения, сколько первоначально было взято argv0 процесса выполнения. Все, что сверх этого, будет усечено.

Эта функция может работать не так, как описано на всех платформах. Обычно это также требует программы ps с наследием BSD. Таким образом, в некоторых версиях Solaris UNIX программа /usr/bin/ps не работает, но работает /usr/ucb/ps. Другие программы, которые могут отображать это значение, включают htop.

Вы могли бы:

Установите display-name меньшей длины:

WSGIDaemonProcess yourapp user=www-data group=www-data processes=5 threads=5 display-name=wsws

И попробуйте найти их:

ps aux | grep wsws

Или установите его в %{GROUP} и отфильтруйте, используя имя группы процессов демона (wsgi: group).

Ответ 2

Способ управления процессами с помощью mod_wsgi для каждого режима описан в:

В встроенном режиме, когда приложение WSGI запускается внутри процессов дочерних сотрудников Apache, Apache управляет процессом, когда процессы создаются и уничтожаются на основе параметров Apache MPM. Из-за того, как Apache управляет процессами, они могут быть отключены в любое время, если есть недостаточная пропускная способность запроса, или больше процессов может быть создано, если увеличивается пропускная способность запроса. При запуске один и тот же процесс будет обрабатывать множество запросов с течением времени, пока он не завершит работу. Другими словами, Apache динамически управляет количеством процессов.

Из-за этого динамического управления процессами, это плохая идея использовать встроенный режим mod_wsgi, если вы не знаете, как правильно настроить Apache и многое другое. Короче говоря, никогда не используйте встроенный режим, если у вас нет достаточного опыта работы с Apache и запущены с ним приложения Python. Вы можете посмотреть видео о том, почему вы не хотите запускать встроенный режим:

Существует также сообщение в блоге:

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

Для режима демона приложение WSGI запускается в отдельном наборе управляемых обработок. Они создаются с самого начала и будут выполняться до тех пор, пока не будет перезапущен Apache, или перезагрузка процесса будет вызвана по различным причинам, в том числе:

  • Процесс демона отправляется прямым сигналом к отключению пользователем.
  • Код приложения отправляет себе сигнал.
  • Изменен файл сценария WSGI, который приведет к выходу из строя, чтобы приложение WSGI можно было перезагрузить.
  • Определенный тайм-аут запроса возникает из-за застревания или длительного запроса.
  • Определено максимальное количество запросов.
  • Определенный тайм-аут бездействия истекает.
  • Определенный таймер для периодического перезапуска процесса истекает.
  • Определен тайм-аут запуска, и приложение WSGI не удалось загрузить за это время.

В этих случаях, когда процесс завершается, он заменяется.

Более подробную информацию о различных параметрах тайм-аута и способах реагирования процессов на сигналы можно найти в:

Более подробную информацию о перезагрузке исходного кода и касании файла сценария WSGI можно найти в:

Один из документированных документов - это способ использования кода, который будет искать любые изменения в файлах кода Python, используемых вашим приложением. Когда происходит смена какого-либо из файлов, процесс будет перезапущен, отправив себе сигнал. Это следует использовать только для разработки и никогда не выпускать.

Если вы используете mod_wsgi-express в разработке, что предпочтительнее вручную настроить Apache самостоятельно, вы можете использовать --reload-on-changes.

Если вы SIGTERM сигнал SIGTERM процессу демона, есть последовательность завершения выключения, где он будет ждать несколько секунд, чтобы дождаться завершения текущих запросов. Если запросы не закончатся, процесс все равно будет завершен. Этот период времени продиктован таймаутом выключения. Вы не должны играть с этим значением.

При отправке сигнала SIGUSR1 процессу демона по умолчанию он действует так же, как отправка сигнала SIGTERM. Если, однако, вы укажете грациозный таймаут для выключения, вы можете продлить, как долго он будет ждать завершения текущих запросов. Новые запросы будут приниматься в течение этого периода. Этот грамотный таймаут также применяется и в других случаях, например, максимальное количество полученных запросов или таймер для периодического перезапуска. Если вам нужен таймаут при использовании SIGUSR1 для разных случаев, определите тайм-аут выселения.

Что касается того, как идентифицировать процессы-демоны для отправки сигнала, используйте display-name опции WSGIDaemonProcess. Затем используйте ps для идентификации процессов или, возможно, используйте killall если он использует измененное имя процесса на вашей платформе. Передача демона обрабатывает сигнал SIGUSR1 если требуется более грациозное завершение работы и SIGTERM если вы хотите, чтобы они немедленно перезапустились.

Если вы хотите отслеживать, как долго работает демон, вы можете использовать:

import mod_wsgi
metrics = mod_wsgi.process_metrics()

Значение metrics будет включать в себя следующий вывод для процесса, в котором выполняется вызов:

{'active_requests': 1,
 'cpu_system_time': 0.009999999776482582,
 'cpu_user_time': 0.05000000074505806,
 'current_time': 1525047105.710778,
 'memory_max_rss': 11767808,
 'memory_rss': 11767808,
 'pid': 4774,
 'request_busy_time': 0.001851,
 'request_count': 2,
 'request_threads': 2,
 'restart_time': 1525047096.31548,
 'running_time': 9,
 'threads': [{'request_count': 2, 'thread_id': 1},
             {'request_count': 1, 'thread_id': 2}]}

Если вы просто хотите узнать, сколько процессов/потоков используется для текущей группы процессов демона, вы можете использовать:

mod_wsgi.process_group
mod_wsgi.application_group
mod_wsgi.maximum_processes
mod_wsgi.threads_per_process

чтобы получить подробную информацию о группе процессов. Число процессов фиксировано в это время для режима демона, а имя maximum_processes просто соответствует тому, что имя находится во встроенном режиме.

Если вам нужно запустить код при завершении процесса, вы НЕ должны пытаться определять свои собственные обработчики сигналов. Сделайте это, и mod_wsgi фактически проигнорирует их, поскольку они будут мешать нормальной работе Apache и mod_wsgi. Вместо этого, если вам нужно запустить код при завершении процесса, используйте atexit.register(). Кроме того, вы можете подписаться на специальные события, созданные mod_wsgi, и вызывать что-то из события завершения процесса.

Ответ 3

Основываясь на ответе Evhz, я сделал простой тест, чтобы проверить, что процессы все еще работают:

Конфигурация Apache:

<VirtualHost *:80>
  ServerName example.com
  WSGIDaemonProcess yourapp user=www-data group=www-data processes=5 threads=5 display-name=testwsgi
  WSGIScriptAlias / /home/www/wsgi_test/app.wsgi
  <Directory /home/www/wsgi_test>
    WSGIProcessGroup yourapp
    WSGIApplicationGroup %{GLOBAL}
    Require all granted
  </Directory>
</VirtualHost>

Файл app.wsgi:

import os, time
from bottle import route, template, default_app

os.chdir(os.path.dirname(__file__))

@route('/hello/<name>')
def index(name):
    global i
    i += 1
    return template('<b>Hello {{name}}</b>! request={{i}}, pid={{pid}}',
        name=name, i=i, pid=os.getpid())

i = 0
time.sleep(3)     # wait 3 seconds to make the client notice we launch a new process!

application = default_app()

Теперь вы часто пользуетесь http://www.example.com/hello/you:

Первоначальный time.sleep(3) поможет в браузере клиента точно увидеть, когда будет запущен новый процесс, и счетчик запросов i позволит увидеть, сколько запросов было подано каждому процессу.

PID будут соответствовать тем, которые присутствуют в ps aux | grep testwsgi ps aux | grep testwsgi:

enter image description here

Также time.sleep(3) будет выполняться максимум 5 раз (при запуске каждого из 5 процессов), тогда процессы должны выполняться вечно, пока мы не перезапустим/остановим сервер или не app.wsgi файл app.wsgi (изменение его триггеров перезапуск 5 процессов, вы можете увидеть новые PID).


[Я проверю это, разрешив мой тестовый прогон, и зайдите через http://www.example.com/hello/you через 2 дня, чтобы увидеть, все ли это был ранее запущенный процесс или новый!]

Изменить: на следующий день те же процессы все еще работали. Теперь, через два дня после перезагрузки одного и того же URL-адреса, я заметил, что были созданы новые процессы... (Есть ли время, после которого умирает процесс без запроса?)