如何从作为 Windows pywin32 服务运行的烧瓶和女服务员干净地退出 [英] How to exit cleanly from flask and waitress running as a windows pywin32 service
问题描述
我已经设法拼凑了一个 pywin32 Windows 服务在 pylons 女服务员 wsgi 服务器内运行烧瓶的工作演示(如下).一个侄女独立的解决方案是这个想法..
I have managed to cobble together a working demo of a pywin32 windows service running flask inside the pylons waitress wsgi server (below). A niece self contained solution is the idea..
我花了几个小时来审查和测试让女服务员干净利落地退出的方法(例如 this 和 this) 但我能做到的最好far 是一种自杀式 SIGINT,它使 Windows 在通过服务控制面板停止时抱怨管道已结束",但至少它会停止:-/我猜 pywin32 启动的 pythonservice.exe 不应终止,只是女服务员踩?
I have spent hours reviewing and testing ways of making waitress exit cleanly (like this and this) but the best I can do so far is a kind of suicidal SIGINT which makes Windows complain "the pipe has been ended" when stopping through the Services control panel, but at least it stops :-/ I guess the pythonservice.exe which pywin32 starts, should not terminate, just the waitress treads?
老实说,我仍然不确定这是否是关于女服务员、pywin32 的问题,或者可能只是简单的 python.我确实感觉答案就在我面前,但现在我完全被难住了.
To be honest I'm still uncertain if this is a question about waitress, pywin32, or maybe its just plain python. I do have the feeling the answer is right in front of me but right now I'm completely stumped.
欢迎提出任何建议!
import os
import random
import signal
import socket
from flask import Flask, escape, request
import servicemanager
import win32event
import win32service
import win32serviceutil
from waitress import serve
app = Flask(__name__)
@app.route('/')
def hello():
random.seed()
x = random.randint(1, 1000000)
name = request.args.get("name", "World")
return 'Hello, %s! - %s - %s' % (escape(name), x, os.getpid())
# based on https://www.thepythoncorner.com/2018/08/how-to-create-a-windows-service-in-python/
class SMWinservice(win32serviceutil.ServiceFramework):
'''Base class to create winservice in Python'''
_svc_name_ = 'WaitressService'
_svc_display_name_ = 'Waitress server'
_svc_description_ = 'Python waitress WSGI service'
@classmethod
def parse_command_line(cls):
'''
ClassMethod to parse the command line
'''
win32serviceutil.HandleCommandLine(cls)
def __init__(self, args):
'''
Constructor of the winservice
'''
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
socket.setdefaulttimeout(60)
def SvcStop(self):
'''
Called when the service is asked to stop
'''
self.stop()
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, ''))
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
'''
Called when the service is asked to start
'''
self.start()
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
self.main()
def start(self):
pass
def stop(self):
print 'sigint'
os.kill(os.getpid(), signal.SIGINT)
def main(self):
print 'serve'
serve(app, listen='*:5000')
if __name__ == '__main__':
SMWinservice.parse_command_line()
推荐答案
我找到了一个使用子线程的解决方案,该解决方案似乎有效.不太确定这是否可能会产生意想不到的后果..
I have found a solution using a sub-thread that seems to work. Not quite sure if this may have possible unintended consequences yet..
--
我相信下面的更新版本,注入"进入女服务员线程的 SystemExit 是最好的.我认为你的原创很难杀死线程,但这个打印线程完成"表示正常关闭.
I believe the updated version below, "injecting" a SystemExit into the waitress thread is as good as it gets. I think thee original kills the thread hard, but this one prints "thread done" indicating a graceful shutdown.
欢迎更正或改进!
import ctypes
import os
import random
import socket
import threading
from flask import Flask, escape, request
import servicemanager
import win32event
import win32service
import win32serviceutil
from waitress import serve
app = Flask(__name__)
# waitress thread exit based on:
# https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/
@app.route('/')
def hello():
random.seed()
x = random.randint(1, 1000000)
name = request.args.get("name", "World")
return 'Hello, %s! - %s - %s' % (escape(name), x, os.getpid())
class ServerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
print('thread start
')
serve(app, listen='*:5000') # blocking
print('thread done
')
def get_id(self):
# returns id of the respective thread
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def exit(self):
thread_id = self.get_id()
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')
class SMWinservice(win32serviceutil.ServiceFramework):
_svc_name_ = 'WaitressService'
_svc_display_name_ = 'Waitress server'
_svc_description_ = 'Python waitress WSGI service'
@classmethod
def parse_command_line(cls):
win32serviceutil.HandleCommandLine(cls)
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.stopEvt = win32event.CreateEvent(None, 0, 0, None)
socket.setdefaulttimeout(60)
def SvcStop(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, ''))
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.stopEvt)
def SvcDoRun(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ''))
self.main()
def main(self):
print('main start')
self.server = ServerThread()
self.server.start()
print('waiting on win32event')
win32event.WaitForSingleObject(self.stopEvt, win32event.INFINITE)
self.server.exit() # raise SystemExit in inner thread
print('waiting on thread')
self.server.join()
print('main done')
if __name__ == '__main__':
SMWinservice.parse_command_line()
这篇关于如何从作为 Windows pywin32 服务运行的烧瓶和女服务员干净地退出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!