如何保持 Windows 服务运行

4
以下是我正在运行的Windows服务框架。如果出现错误,该错误将被记录并可以在事件查看器中查看。问题在于脚本仅停止而不重新启动,即使我已将恢复设置为在第一次、第二次和后续故障时重新启动服务。目前,我的错误处理很少,因为我想在事件查看器中查看可能出现的错误,以便编写代码来相应地处理这些错误。
from win32api import CloseHandle, GetLastError, SetConsoleCtrlHandler
import os
import sys 
import time
import pythoncom
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket

class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "my_service_name"
    _svc_display_name_ = "my service"

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

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.run = False

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))
        self.run = True
        self.main()

    def main(self):
        while self.run == True
            pass          

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

编辑:

我尝试在self.main周围使用try:except,但结果仍然相同。当服务崩溃时,它没有重新启动...请问是否有任何想法?如果无法在崩溃事件中重新启动服务,则服务并不是特别有用...最好将其作为.pyc运行

编辑:

以下是我的脚本中可能出现的错误示例...我不认为此错误消息特别有用,因为我要实现的目标是使服务重新启动,但这里是一个导致我的服务崩溃而没有重新启动的错误示例:

The instance's SvcRun() method failed 
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\win32\lib\win32serviceutil.py", line 806, in SvcRun
    self.SvcDoRun()
  File "C:\Some_Service.py", line 46, in SvcDoRun
    self.main()
  File "Some_Service.py", line 61, in main
    ser = self.open_serial_port()
  File "Some_Service.py", line 70, in open_serial_port
    serial_connection.open()
  File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 56, in open
    raise SerialException("could not open port %s: %s" % (self.portstr, ctypes.WinError()))
SerialException: could not open port COM6: [Error 1225] The remote system refused the network connection. 
%2: %3

导致它崩溃的错误是什么?除了在main()中不休眠外,您的代码看起来可用。 - AlG
@qor72 这只是一个框架,用于将脚本转换为服务。我正在运行的脚本用于解析串行数据。问题在于可能会发生许多错误,例如服务可能无法连接到我的数据库。现在我想让它休眠几秒钟,然后再次启动服务。如果我已经将服务设置为在失败时重新启动,那么我不确定为什么它会死掉而不重新启动.. 我肯定是漏掉了某个处理代码的部分? - Richard
在我编写的服务中,不是用Python编写的,错误检查会将错误写入日志并继续运行而不需要重新启动。这就是为什么我对你的信息感到好奇;例如,是服务无法写入错误日志,从而导致了破坏您的服务的错误吗? - AlG
@qor72,我刚刚弄明白了...这里的答案给了我一些启示。https://dev59.com/-XVC5IYBdhLWcg3wqzDV 。所以在Python中,我需要执行os._exit(1)或者其他非零的退出方式。BiggAl的回答接近了,但是缺少了os._exit(-1)而不是return -1。 - Richard
3个回答

8
以下是一个服务,它只是通过除以零来引发错误。如果出现错误,则发送事件并使用os._exit(-1)退出服务。该值必须为非0,以便Windows知道服务没有正常退出。
from win32api import CloseHandle, GetLastError, SetConsoleCtrlHandler
import os
import sys
import time

import win32serviceutil
import win32service
import win32event
import servicemanager

import traceback



class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "test"
    _svc_display_name_ = "test"

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




    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.run = False
    def SvcDoRun(self):

        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))
        self.run = True
        try: # try main
            self.main()
        except:
            servicemanager.LogErrorMsg(traceback.format_exc()) # if error print it to event log
            os._exit(-1)#return some value other than 0 to os so that service knows to restart


    def main(self):

        while self.run == True:
            time.sleep(30)         
            t = 1/0

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

4
使用os._exit(-1)而不是sys.exit(-1)的原因是os._exit会立即终止进程,而sys.exit则引发SystemExit异常并在退出之前执行清理和其他操作。 - Jon Nalley

0
为什么不使用 try-except 呢?
class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "my_service_name"
    _svc_display_name_ = "my service"

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

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.run = False

    def SvcDoRun(self):
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_,''))
        self.run = True
        self.main()

    def main(self):
        while self.run == True
            try :
                <your code>
            except :
                time.sleep(3)
                <may be an error log here>



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

在我的答案中,我是这样做的。就在我调用main函数的地方,而不是在函数本身中。 - Richard

0

我怀疑你的服务实际上并没有崩溃,而是在运行之前就已经完成了。你尝试在main函数中插入几秒钟的睡眠语句了吗?


主函数是一个无限循环,直到服务接收到停止命令。实际上,脚本会崩溃,因为导致崩溃的错误已被记录。如果出现错误,我希望该服务能够重新启动... - Richard
你的错误信息是什么?在崩溃的同时它是否仍然返回错误代码0?也许你需要类似于以下的内容:try:\n main()\nexcept:\n return -1 - theheadofabroom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接