Как создать службу Celery Windows? - PullRequest
9 голосов
/ 21 февраля 2012

Я пытаюсь создать службу Windows для запуска Celery. Я наткнулся на статью, в которой это делается с помощью Task Scheduler . Тем не менее, кажется, запускает множество экземпляров сельдерея и продолжает поглощать память, пока машина не умрет. Есть ли способ запустить его как службу Windows?

Ответы [ 5 ]

12 голосов
/ 21 февраля 2012

Я получил ответ с другого сайта.Celeryd (служба демона для Celery) запускается как приложение paster, поиск «Служба Windows Paster» приводит меня сюда .Он описывает, как запустить приложение Pylons в качестве службы Windows.Будучи новичком в Paster Framework и размещении веб-сервисов Python, мне не пришло в голову проверить его сначала.Но это решение работает для Celery с небольшими изменениями в сценарии.

Я изменил сценарий, чтобы упростить изменение настроек Celery.Существенные изменения:

  1. Создание файла INI с настройками службы Celery (показано ниже)
  2. Создание сценария Python для создания службы Windows.

Настройки INI-файла (celeryd.ini):

[celery:service]
service_name = CeleryService
service_display_name = Celery Service
service_description = WSCGI Windows Celery Service
service_logfile = celeryd.log

Python-скрипт для создания службы Windows (CeleryService.py):

"""
The most basic (working) Windows service possible.
Requires Mark Hammond's pywin32 package.  
Most of the code was taken from a  CherryPy 2.2 example of how to set up a service
"""
import pkg_resources
import win32serviceutil
from paste.script.serve import ServeCommand as Server
import os, sys
import ConfigParser

import win32service
import win32event

SCRIPT_DIR          = os.path.abspath(os.path.dirname(__file__))
INI_FILE            = 'celeryd.ini'
SERV_SECTION        = 'celery:service'
SERV_NAME           = 'service_name'
SERV_DISPLAY_NAME   = 'service_display_name'
SERV_DESC           = 'service_description'
SERV_LOG_FILE       = 'service_logfile'
SERV_APPLICATION    = 'celeryd'
SERV_LOG_FILE_VAR   = 'CELERYD_LOG_FILE'

# Default Values
SERV_NAME_DEFAULT           = 'CeleryService'
SERV_DISPLAY_NAME_DEFAULT   = 'Celery Service'
SERV_DESC_DEFAULT           = 'WSCGI Windows Celery Service'
SERV_LOG_FILE_DEFAULT       = r'D:\logs\celery.log'

class DefaultSettings(object):
    def __init__(self):
        if SCRIPT_DIR:
            os.chdir(SCRIPT_DIR)
        # find the ini file
        self.ini = os.path.join(SCRIPT_DIR,INI_FILE)
        # create a config parser opject and populate it with the ini file
        c = ConfigParser.SafeConfigParser()
        c.read(self.ini)
        self.c = c

    def getDefaults(self):
        '''
        Check for and get the default settings
        '''
        if (
            (not self.c.has_section(SERV_SECTION)) or
            (not self.c.has_option(SERV_SECTION, SERV_NAME)) or
            (not self.c.has_option(SERV_SECTION, SERV_DISPLAY_NAME)) or
            (not self.c.has_option(SERV_SECTION, SERV_DESC)) or
            (not self.c.has_option(SERV_SECTION, SERV_LOG_FILE))
            ):
            print 'setting defaults'
            self.setDefaults()
        service_name = self.c.get(SERV_SECTION, SERV_NAME)
        service_display_name = self.c.get(SERV_SECTION, SERV_DISPLAY_NAME)
        service_description = self.c.get(SERV_SECTION, SERV_DESC)
        iniFile = self.ini
        service_logfile = self.c.get(SERV_SECTION, SERV_LOG_FILE)
        return service_name, service_display_name, service_description, iniFile, service_logfile

    def setDefaults(self):
        '''
        set and add the default setting to the ini file
        '''
        if not self.c.has_section(SERV_SECTION):
            self.c.add_section(SERV_SECTION)
        self.c.set(SERV_SECTION, SERV_NAME, SERV_NAME_DEFAULT)
        self.c.set(SERV_SECTION, SERV_DISPLAY_NAME, SERV_DISPLAY_NAME_DEFAULT)
        self.c.set(SERV_SECTION, SERV_DESC, SERV_DESC_DEFAULT)
        self.c.set(SERV_SECTION, SERV_LOG_FILE, SERV_LOG_FILE_DEFAULT)
        cfg = file(self.ini, 'wr')
        self.c.write(cfg)
        cfg.close()
        print '''
you must set the celery:service section service_name, service_display_name,
and service_description options to define the service 
in the %s file
''' % self.ini
        sys.exit()


class CeleryService(win32serviceutil.ServiceFramework):
    """NT Service."""

    d = DefaultSettings()
    service_name, service_display_name, service_description, iniFile, logFile = d.getDefaults()

    _svc_name_ = service_name
    _svc_display_name_ = service_display_name
    _svc_description_ = service_description

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        # create an event that SvcDoRun can wait on and SvcStop
        # can set.
        self.stop_event = win32event.CreateEvent(None, 0, 0, None)

    def SvcDoRun(self):
        os.chdir(SCRIPT_DIR)
        s = Server(SERV_APPLICATION)
        os.environ[SERV_LOG_FILE_VAR] = self.logFile
        s.run([self.iniFile])
        win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        #win32event.SetEvent(self.stop_event)
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        sys.exit()

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(CeleryService)

Чтобы установить службу, запустите python CeleryService.py installа затем python CeleryService.py start, чтобы запустить службу. ПРИМЕЧАНИЕ : эти команды должны запускаться в командной строке с правами администратора.

Если необходимо удалить службу, запустите python CeleryService.py remove.

Я пыталсяхост Celery как часть улучшения моей установки RhodeCode.Это решение, кажется, работает.Надеюсь, это кому-нибудь поможет.

4 голосов
/ 08 сентября 2015

Принятый ответ не распространяется на работающий сельдерей с приложением Django.Но это вдохновило меня на создание решения для запуска сельдерея в качестве службы Windows с Django.Обратите внимание, что следующее только для проектов Django.Он может работать с другими приложениями с некоторыми модификациями.

Создайте файл celery_service.py (или что угодно) в папке верхнего уровня вашего проекта Django, того же уровня, что manage.py, со следующим содержимым:

'''Usage : python celery_service.py install (start / stop / remove)
Run celery as a Windows service
'''
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import subprocess
import sys
import os
import shlex
import logging
import time

# The directory for celery.log and celery_service.log
# Default: the directory of this script
INSTDIR = os.path.dirname(os.path.realpath(__file__))
# The path of python Scripts
# Usually it is in PYTHON_INSTALL_DIR/Scripts. e.g.
# r'C:\Python27\Scripts'
# If it is already in system PATH, then it can be set as ''
PYTHONSCRIPTPATH = ''
# The directory name of django project
# Note: it is the directory at the same level of manage.py
# not the parent directory
PROJECTDIR = 'proj'

logging.basicConfig(
    filename = os.path.join(INSTDIR, 'celery_service.log'),
    level = logging.DEBUG, 
    format = '[%(asctime)-15s: %(levelname)-7.7s] %(message)s'
)

class CeleryService(win32serviceutil.ServiceFramework):

    _svc_name_ = "Celery"
    _svc_display_name_ = "Celery Distributed Task Queue Service"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)           

    def SvcStop(self):
        logging.info('Stopping {name} service ...'.format(name=self._svc_name_))        
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        sys.exit()           

    def SvcDoRun(self):
        logging.info('Starting {name} service ...'.format(name=self._svc_name_))
        os.chdir(INSTDIR) # so that proj worker can be found
        logging.info('cwd: ' + os.getcwd())
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)
        command = '"{celery_path}" -A {proj_dir} worker -f "{log_path}" -l info'.format(
            celery_path=os.path.join(PYTHONSCRIPTPATH, 'celery.exe'),
            proj_dir=PROJECTDIR,
            log_path=os.path.join(INSTDIR,'celery.log'))
        logging.info('command: ' + command)
        args = shlex.split(command)
        proc = subprocess.Popen(args)
        logging.info('pid: {pid}'.format(pid=proc.pid))
        self.timeout = 3000
        while True:
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            if rc == win32event.WAIT_OBJECT_0:
                # stop signal encountered
                # terminate process 'proc'
                PROCESS_TERMINATE = 1
                handle = win32api.OpenProcess(PROCESS_TERMINATE, False, proc.pid)
                win32api.TerminateProcess(handle, -1)
                win32api.CloseHandle(handle)                
                break

if __name__ == '__main__':
   win32serviceutil.HandleCommandLine(CeleryService)

Перед запуском скрипта необходимо

  1. Установить pywin32 .

  2. Правильно установите PYTHONSCRIPTPATH ​​и PROJECTDIR в celery_service.py

PYTHONSCRIPTPATH ​​- это обычно папка «Сценарии» в пути установки вашего питона,

например, C: \ Python27 \ Scripts

Либо добавьте его в PATH вашей системы,

, либо отредактируйте celery_service.py

PYTHONSCRIPTPATH = r'C:\Python27\Scripts'

PROJECTDIR - это имя каталогапроект Django.

Это каталог на том же уровне manage.py, а не родительский каталог.

Теперь вы можете установить / запустить / остановить / удалить службу с помощью:

python celery_service.py install
python celery_service.py start
python celery_service.py stop
python celery_service.py remove

Я создал демонстрационный проект Django с сельдереем, работающим в качестве службы Windows:

https://github.com/azalea/django_celery_windows_service

На случай, если вы заинтересованы в работающем примере.

1 голос
/ 12 мая 2016
Ответ

@ azalea мне очень помог, но одна вещь, которую я хотел бы подчеркнуть, это то, что служба (celery_service.py) должна быть установлена ​​с вашим именем пользователя / паролем, в противном случае, когда вы запускаете функцию subprocess.Popen(args) in SvcDoRun(),ничего не произойдет, так как будет разрешение проблемы.Чтобы установить имя пользователя / пароль, вы можете выбрать один из двух способов:

  1. Используя командную строку:

    python33 .\celeryService1.py --username .\USERNAME --password PASSWORD
    
  2. Перейти к управлению компьютером(локальный)> Службы и приложения> Службы, найдите свой сервер (в примере @ azalea это «Служба очереди распределенных задач Celery») и щелкните правой кнопкой мыши, чтобы открыть страницу свойств, введите «Эта учетная запись» на вкладке «Вход в систему» ​​

0 голосов
/ 15 мая 2019

Благодаря Азалии, которая привела меня к тому, что я смог создать 2 службы Windows с Celery 4 для Windows.

Один, чтобы иметь возможность запускать / останавливать несколько рабочих T Во-вторых, чтобы иметь возможность запускать / останавливать службу ритма и убирать пид с помощью Celery 4.

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

Workers.py:

'''Usage : python celery_service.py install (start / stop / remove)
Run celery as a Windows service
'''
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import subprocess
import sys
import os
import shlex
import logging
import time

# The directory for celery_worker.log and celery_worker_service.log
# Default: the directory of this script
INSTDIR = 'X:\Application\Project'
LOGDIR = 'X:\Application\LogFiles'
# The path of python Scripts
# Usually it is in PYTHON_INSTALL_DIR/Scripts. e.g.
# r'C:\Python27\Scripts'
# If it is already in system PATH, then it can be set as ''
PYTHONSCRIPTPATH = 'C:\Python36\Scripts'
# The directory name of django project
# Note: it is the directory at the same level of manage.py
# not the parent directory
PROJECTDIR = 'Project'

logging.basicConfig(
    filename = os.path.join(LOGDIR, 'celery_worker_service.log'),
    level = logging.DEBUG, 
    format = '[%(asctime)-15s: %(levelname)-7.7s] %(message)s'
)

class CeleryService(win32serviceutil.ServiceFramework):

    _svc_name_ = "CeleryWorkers"
    _svc_display_name_ = "CeleryWorkers"

    def __init__(self, args): 
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

    def SvcStop(self):
        logging.info('Stopping {name} service ...'.format(name=self._svc_name_))        
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        logging.info('Stopped1 {name} service ...'.format(name=self._svc_name_))   
        logging.info('Stopped3 {name} service ...'.format(name=self._svc_name_)) 
        command = '"{celery_path}" -A {proj_dir} --workdir=X:/Application/Project control shutdown --timeout=10'.format(
        celery_path=os.path.join(PYTHONSCRIPTPATH, 'celery.exe'),
        proj_dir=PROJECTDIR,
        log_path=os.path.join(LOGDIR,'celery_worker.log'))
        logging.info('command: ' + command)
        args = shlex.split(command)
        proc = subprocess.Popen(args)
        logging.info('Stopped celery shutdown  ...') 
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        logging.info('Stopped2 {name} service ...'.format(name=self._svc_name_))  
        sys.exit()           

    def SvcDoRun(self):
        logging.info('Starting {name} service ...'.format(name=self._svc_name_))
        os.chdir(INSTDIR) # so that proj worker can be found
        logging.info('cwd: ' + os.getcwd())
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)
        command = '"{celery_path}" -A {proj_dir} -c 8 worker --workdir=X:/Application/Project --pidfile=celeryservice.pid  -f "{log_path}" -l info'.format(
            celery_path=os.path.join(PYTHONSCRIPTPATH, 'celery.exe'),
            proj_dir=PROJECTDIR,
            log_path=os.path.join(LOGDIR,'celery_worker.log'))
        logging.info('command: ' + command)
        args = shlex.split(command)
        proc = subprocess.Popen(args)
        logging.info('pid: {pid}'.format(pid=proc.pid))
        self.timeout = 3000
        while True:
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            if rc == win32event.WAIT_OBJECT_0:
                # stop signal encountered
                # terminate process 'proc'
                PROCESS_TERMINATE = 1
                handle = win32api.OpenProcess(PROCESS_TERMINATE, False, proc.pid)
                win32api.TerminateProcess(handle, -1)
                win32api.CloseHandle(handle)                
                break

if __name__ == '__main__':
   win32serviceutil.HandleCommandLine(CeleryService)

Beatservice.py:

'''Usage : python celery_service.py install (start / stop / remove)
Run celery as a Windows service
'''
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import subprocess
import sys
import os
import shlex
import logging
import time
import signal

# The directory for celery_beat.log and celery_beat_service.log
# Default: the directory of this script
INSTDIR = os.path.dirname(os.path.realpath(__file__))
LOGPATH = 'X:\Application\Logs'
# The path of python Scripts
# Usually it is in PYTHON_INSTALL_DIR/Scripts. e.g.
# r'C:\Python27\Scripts'
# If it is already in system PATH, then it can be set as ''
PYTHONSCRIPTPATH = 'C:\Python36\Scripts'
# The directory name of django project
# Note: it is the directory at the same level of manage.py
# not the parent directory
PROJECTDIR = 'PROJECT'

logging.basicConfig(
    filename = os.path.join(LOGPATH, 'celery_beat_service.log'),
    level = logging.DEBUG, 
    format = '[%(asctime)-15s: %(levelname)-7.7s] %(message)s'
)

class CeleryService(win32serviceutil.ServiceFramework):

    _svc_name_ = "CeleryBeat"
    _svc_display_name_ = "CeleryBeat"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)           

    def SvcStop(self):
        logging.info('Stopping 1 {name} service ...'.format(name=self._svc_name_))        
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        pidno = open("X:\Aplication\Project\celerybeat.pid", "r")
        _pid_id_ = pidid=pidno.read()
        pidno.close()
        logging.info(_pid_id_)
        logging.info('taskkill /F /PID {pidid} ..'.format(pidid=_pid_id_))
        cmdcom = 'taskkill /F /PID {pidid}'.format(pidid=_pid_id_)
        logging.info(cmdcom)
        killargs = shlex.split(cmdcom)
        process = subprocess.Popen(killargs)
        output, error = process.communicate()
        logging.info(output)
        logging.info('Stopping 2 {name} service ...'.format(name=self._svc_name_))
        os.remove("X:\Application\PROJECT\celerybeat.pid")
        logging.info('X:\Application\PROJECT\celerybeat.pid  file removed')
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)
        sys.exit()

    def SvcDoRun(self):
        logging.info('Starting {name} service ...'.format(name=self._svc_name_))
        os.chdir(INSTDIR) # so that proj worker can be found
        logging.info('cwd: ' + os.getcwd())
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)
        command = '"{celery_path}" -A {proj_dir} beat --workdir=X:/Application/Project -f X:/Application/logs/beat.log -l info'.format(
            celery_path=os.path.join(PYTHONSCRIPTPATH, 'celery.exe'),
            proj_dir=PROJECTDIR,
            log_path=os.path.join(LOGPATH,'celery_beat.log'))
        logging.info('command: ' + command)
        args = shlex.split(command)
        proc = subprocess.Popen(args)
        logging.info('pid: {pid}'.format(pid=proc.pid))
        self.timeout = 3000
        while True:
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            if rc == win32event.WAIT_OBJECT_0:
                # stop signal encountered
                # terminate process 'proc'
                PROCESS_TERMINATE = 1
                handle = win32api.OpenProcess(PROCESS_TERMINATE, False, proc.pid)
                win32api.TerminateProcess(handle, -1)
                win32api.CloseHandle(handle)                
                break

if __name__ == '__main__':
   win32serviceutil.HandleCommandLine(CeleryService)
0 голосов
/ 03 сентября 2018

Хороший проект, но его не удалось использовать: Ссылка на GitHub из django-windows-tools . Это дало мне тайм-аут в последней командной строке. Не хватает времени для поиска почему.

Пакет позволяет настроить файлы FastCGI, Celery и Static для проекта Django на IIS.

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