CherryPy作为Windows服务 - 示例

1 投票
2 回答
3829 浏览
提问于 2025-04-20 15:06

我看到很多人都在为这个问题苦恼,我自己也遇到了一些麻烦。如果有人能帮我让CherryPy服务的例子正常工作,那就太好了。能解释一下遇到的问题就更感谢了。

在这里有一个关于CherryPy作为Windows服务的例子,链接在这里:CherryPy Wiki。我正在尝试让它工作。以下是我的代码:

"""
The most basic (working) CherryPy 3.0 Windows service possible.
Requires Mark Hammond's pywin32 package.
"""

import cherrypy
import win32serviceutil
import win32service
import win32event
import servicemanager

class HelloWorld:
    """ Sample request handler class. """

    def index(self):
        return "Hello world!"
    index.exposed = True


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

    _svc_name_ = "CherryPyService"
    _svc_display_name_ = "CherryPy Service"

    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):
        self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))

        cherrypy.tree.mount(HelloWorld(), '/')

        # in practice, you will want to specify a value for
        # log.error_file below or in your config file.  If you
        # use a config file, be sure to use an absolute path to
        # it, as you can't be assured what path your service
        # will run in.
        cherrypy.config.update({
            'global':{
                'engine.autoreload.on': False,
                'log.screen': False,
                'log.error_file': 'c:\\MG\\temp\\CherryPy_Sample_Service.log',
                'engine.SIGHUP': None,
                'engine.SIGTERM': None
                }
            })
        # set blocking=False so that start() does not block
        cherrypy.server.quickstart()
        cherrypy.engine.start(blocking=False)
        # now, block until our event is set...
        win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE)
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        cherrypy.server.stop()
        win32event.SetEvent(self.stop_event)

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

我的代码和链接中的例子有些不同。我添加了:

  • self.ReportServiceStatus(win32service.SERVICE_START_PENDING),这是在开始的时候报告服务状态,
  • self.ReportServiceStatus(win32service.SERVICE_RUNNING),这是在最后报告服务状态,放在SvcDoRun的第一和最后一句。
  • 'log.error_file': 'c:\\MG\\temp\\CherryPy_Sample_Service.log', 这是按照维基的指示添加的。

重要说明:虽然可以用控制台命令python cherrypy_sample_service.py install来安装服务,但不能python cherrypy_sample_service.py start命令来启动它。原因是这样创建的服务会指向python可执行文件,而这个文件并不是为服务设计的。

所以为了进一步测试,我用以下方式编译了代码:

from cx_Freeze import setup, Executable

exe = Executable(
    script='cherrypy_sample_service.py'
)


build_options = {'includes': ['cherrypy', 'win32serviceutil', 'win32service', 'win32event', 'servicemanager']}

setup(
        name = "CherryPy Sample Service",
        version = "0.1",
        service = ["cherrypy_sample_service.py"],
        options = {"build_exe" : build_options},
        executables = [exe])

在构建过程中,我收到了以下警告:

Python27\App\lib\distutils\dist.py:267: UserWarning: Unknown distribution option: 'service'
warnings.warn(msg)

我根据这个StackOverflow上的问题回答添加了这个选项。

现在我可以调用cherrypy_sample_service installcherrypy_sample_service removecherrypy_sample_service update。但是尝试运行服务(无论是从服务管理器还是通过cherrypy_sample_service start)都会出现以下错误:

Error starting service: The service did not respond to the start or control request in a timely fashion.

我现在卡住了。日志文件甚至都没有被创建。请问能帮我让这个例子运行起来吗?我想如果我们能有一个解释清楚并且能运行的例子,对其他遇到类似问题的人也会很有帮助。谢谢!

2 个回答

1

我刚从你提到的维基页面上下载了3.1版本,结果一切都正常。也就是说,它可以顺利安装、启动和停止,就像通过服务管理界面操作一样。

"""
The most basic (working) CherryPy 3.1 Windows service possible.
Requires Mark Hammond's pywin32 package.
"""

import cherrypy
import win32serviceutil
import win32service

class HelloWorld:
    """ Sample request handler class. """

    @cherrypy.expose
    def index(self):
        return "Hello world!"


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

    _svc_name_ = "CherryPyService"
    _svc_display_name_ = "CherryPy Service"

    def SvcDoRun(self):
        cherrypy.tree.mount(HelloWorld(), '/')

        # in practice, you will want to specify a value for
        # log.error_file below or in your config file.  If you
        # use a config file, be sure to use an absolute path to
        # it, as you can't be assured what path your service
        # will run in.
        cherrypy.config.update({
            'global':{
                'log.screen': False,
                'engine.autoreload.on': False,
                'engine.SIGHUP': None,
                'engine.SIGTERM': None
                }
            })

        cherrypy.engine.start()
        cherrypy.engine.block()

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        cherrypy.engine.exit()

        self.ReportServiceStatus(win32service.SERVICE_STOPPED) 
        # very important for use with py2exe
        # otherwise the Service Controller never knows that it is stopped !

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

我的测试环境是:XP SP3,Python 2.7.5,CherryPy 3.3.0,pywin32 218.4。

调试小贴士

运行你的服务并不需要构建一个二进制文件。当你从源代码安装pywin32服务时,它会使用 %PYDIR%\Lib\site-packages\win32\pythonservice.exe 作为服务的代理。也就是说,所有服务的运行命令行都是这个,并且 REG:\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CherryPyService\PythonClass 定义了服务的实际Python类。

你可以在命令行中运行 pythonservice.exe,确保它没有错误地运行 (1)。你可能还会注意到有一个调试选项,可以将服务的标准输出和错误输出连接到终端 (2)。

确保没有防火墙软件阻止 pythonservice.exe 进行网络活动。

1

最后我找到了解决办法 - 我下载并重新安装了pywin32... 一切都运行得很好! :)

一些最后的提示:

  • 服务可以直接从编辑器启动 - 不需要编译。
  • cherrypy.config.update 中添加 'log.error_file': 'c:\\somedir\cherypy_sample_service.log' 可以帮助确认服务是否正常运行。

在尝试调试问题时,我发现一旦 win32serviceutil.HandleCommandLine 调用 StartService 并到达这段代码:

    try:
        win32service.StartService(hs, args)
    finally:
        win32service.CloseServiceHandle(hs)

我无法进入 win32service.StartService 的内部。我找不到这个文件,所以我重新安装了pywin32。

我只希望能有一些错误信息 - 希望这对其他人有帮助。

撰写回答