如何从作为Windows pywin32服务运行的烧瓶和女服务员干净地退出 [英] How to exit cleanly from flask and waitress running as a windows pywin32 service

查看:111
本文介绍了如何从作为Windows pywin32服务运行的烧瓶和女服务员干净地退出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我设法在pylons女服务员wsgi服务器(下面)中运行了一个运行flask的pywin32 Windows服务的演示示例.一个侄女自给自足的解决方案就是这个主意.

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..

我花了数小时来审查和测试使女服务员干净出口的方法(例如),但我能做到的最好到目前为止,是一种自杀性的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\n')
        serve(app, listen='*:5000')  # blocking
        print('thread done\n')

    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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆