Tôi đang cố tạo một Dịch vụ Windows để khởi chạy Celery. Tôi đã đi qua một bài viết mà nó sử dụng Task Scheduler. Tuy nhiên nó dường như khởi động nhiều trường hợp cần tây và tiếp tục ăn bộ nhớ cho đến khi máy chết. Có cách nào để khởi chạy nó như một dịch vụ Windows không?Cách tạo Dịch vụ Windows Celery?
Trả lời
Tôi nhận được câu trả lời từ một trang web khác. Celeryd (dịch vụ daemon cho Celery) chạy như một ứng dụng paster, tìm kiếm 'Paster Windows Service' dẫn tôi here. Nó mô tả cách chạy ứng dụng Giá treo như một Dịch vụ Windows. Là người mới với khuôn khổ paster và lưu trữ các dịch vụ web python, nó không vượt qua tâm trí của tôi để kiểm tra nó lúc đầu. Nhưng giải pháp đó làm việc cho Celery với một chút thay đổi ở đây và trong kịch bản.
Tôi đã sửa đổi tập lệnh để dễ dàng sửa đổi cài đặt Celery hơn. Những thay đổi quan trọng là:
- Tạo một tập tin INI với các thiết lập cho dịch vụ cần tây (hình dưới đây)
- Tạo một kịch bản python để tạo ra một dịch vụ Windows.
tập tin INI (celeryd.ini):
[celery:service]
service_name = CeleryService
service_display_name = Celery Service
service_description = WSCGI Windows Celery Service
service_logfile = celeryd.log
Python script để tạo ra Windows Service (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)
Để cài đặt chạy dịch vụ python CeleryService.py install
và sau đó python CeleryService.py start
để bắt đầu dịch vụ. LƯU Ý: Các lệnh này sẽ được chạy trong dòng lệnh với quyền quản trị viên.
Nếu dịch vụ cần được xóa, hãy chạy python CeleryService.py remove
.
Tôi đã cố gắng để lưu trữ Celery như là một phần của việc nâng cấp cài đặt RhodeCode của tôi. Giải pháp này dường như hoạt động. Hy vọng điều này sẽ giúp một ai đó.
Câu trả lời được chấp nhận không áp dụng cho việc chạy cần tây bằng ứng dụng Django. Nhưng nó đã truyền cảm hứng cho tôi để đưa ra một giải pháp cho việc chạy cần tây như một dịch vụ Windows với Django. Lưu ý rằng sau đây chỉ dành cho các dự án Django. Nó có thể làm việc với các ứng dụng khác với một số sửa đổi.
Tạo một celery_service.py tệp (hoặc bất cứ điều gì bạn thích) bên trong thư mục cấp cao nhất dự án Django của bạn, mức độ tương tự như manage.py, với các nội dung sau đây:
'''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)
Trước khi kịch bản có thể được chạy, bạn cần phải
Cài đặt pywin32.
đúng thiết PYTHONSCRIPTPATH và PROJECTDIR trong celery_service.py
PYTHONSCRIPTPATH thường là "Scripts" thư mục theo đường dẫn cài đặt của python của bạn,
ví dụC: \ Python27 \ Scripts
Hoặc thêm nó vào PATH của hệ thống của bạn,
hoặc chỉnh sửa celery_service.py
PYTHONSCRIPTPATH = r'C:\Python27\Scripts'
PROJECTDIR là tên thư mục của dự án Django.
Đây là thư mục ở cùng cấp độ manage.py, không phải thư mục chính.
Bây giờ bạn có thể cài đặt/start/stop/loại bỏ các dịch vụ với:
python celery_service.py install
python celery_service.py start
python celery_service.py stop
python celery_service.py remove
Tôi tạo ra một dự án Django demo với cần tây chạy như một dịch vụ Windows:
https://github.com/azalea/django_celery_windows_service
Trong trường hợp bạn quan tâm đến một ví dụ đang chạy.
Có lẽ tôi đang thiếu một cái gì đó, nhưng ngay cả trong tài liệu mới nhất, có vẻ như 'celeryd' vẫn đang được sử dụng, mặc dù nó cũng đề cập đến việc sử dụng Django để ' daemonize 'Celery: http://ask.github.io/celery/cookbook/daemonizing.html#init-script-celeryd –
@ViteFalcon Cảm ơn bạn đã chỉ ra. Tôi đã chỉnh sửa từ ngữ của mình. Vui lòng chỉnh sửa và cải thiện nó. – azalea
Câu trả lời của @azalea đã giúp tôi rất nhiều, nhưng một điều tôi muốn nhấn mạnh ở đây là dịch vụ (celery_service.py) cần được cài đặt với người dùng/mật khẩu của bạn, nếu không, khi bạn chạy chức năng subprocess.Popen(args) in SvcDoRun()
, không có gì sẽ xảy ra vì sẽ có sự cố về quyền. Để thiết lập người dùng/mật khẩu, bạn có thể chọn một trong hai phương pháp:
Sử dụng dòng lệnh:
python33 .\celeryService1.py --username .\USERNAME --password PASSWORD
Đến Computer Management (địa phương)> Dịch vụ và Ứng dụng> Services, tìm bạn server (trong ví dụ của @ azalea, đó là "Celery Distributed Task Queue Service"), và nhấn chuột phải để mở trang Properties, nhập "Tài khoản này" trong tab Log On
Downvoter (s) vui lòng cho biết lý do bạn downvoted . –
Làm thế nào để bạn thiết lập một nhà môi giới trong trường hợp như vậy, Redis ví dụ –