如何创build芹菜Windows服务?

我正在尝试创build一个Windows服务来启动芹菜。 我遇到了一个使用Task Scheduler的文章。 然而,它似乎启动了大量的芹菜实例,并一直记忆直到机器死亡。 有没有办法将其作为Windows服务启动?

我从另一个网站得到了答案。 Celeryd(Celery的守护程序服务)作为贴纸应用程序运行,搜索“Paster Windows Service” 在这里引导我。 它描述了如何运行一个Pylons应用程序作为Windows服务。 作为paster框架和托管python web服务的新手,我并没有想到要在第一时间检查它。 但是这个解决方案适用于Celery,而且脚本中的内容稍有变化。

我已经修改了脚本,以便更改Celery设置。 基本的变化是:

  1. 使用Celery服务的设置创建一个INI文件(如下所示)
  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 

用于创建Windows服务的Python脚本(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安装的一部分。 这个解决方案似乎工作。 希望这会帮助别人。

接受的答案不适用于使用Django应用程序运行芹菜。 但是这启发了我想出了一个解决方案,使用Django作为Windows服务运行芹菜。 请注意,以下仅适用于Django项目。 它可能会与其他应用程序进行一些修改。

在Django项目的顶级文件夹(与manage.py同级)中创建一个文件celery_service.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. eg # 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. 在celery_service.py中正确设置PYTHONSCRIPTPATH和PROJECTDIR

PYTHONSCRIPTPATH通常是python安装路径下的“Scripts”文件夹,

例如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 

我使用作为Windows服务运行的芹菜创建了一个演示Django项目:

https://github.com/azalea/django_celery_windows_service

如果你对一个正在运行的例子感兴趣。

@azalea的答案帮了我很大的忙,但是我要强调的一点是,服务(celery_service.py)需要用你的用户名/密码来安装,否则当你subprocess.Popen(args) in SvcDoRun()运行subprocess.Popen(args) in SvcDoRun()函数,没有什么会发生,因为会有一个权限问题。 要设置用户/密码,您可以选择以下两种方法之一:

  1. 使用命令行:

     python33 .\celeryService1.py --username .\USERNAME --password PASSWORD 
  2. 转到计算机管理(本地)>服务和应用程序>服务,找到你的服务器(在@ azalea的例子中,它是“芹菜分布式任务队列服务”),右键点击打开属性页面,在登录输入“This account”标签