Python으로 Windows Service 제작해보기
지난 주에 breakpad로 간단한 크래시 리포팅 툴을 작성했다. 이 리포트를 받는 툴을 python으로 작성했는데, 이걸 서비스로 띄우기 위해 어제 했던 삽질을 간략히(?) 정리 해보는 차원에서 글 하나.
우선 아주 간단한 웹 서버를 예로 쓰겠다. localhost:8000에서 HTTP GET으로 요청이 들어오면, 이 GET 경로를 그대로 text/plain으로 보내주는 서버다. 예를 들자면 http://127.0.0.1/path;param?var=value#frag
요청에 대해서 /path;param?var=value
가 웹 페이지에 찍힌다.
import BaseHTTPServer
from SocketServer import ThreadingTCPServer
class EchoHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header(‘Content-Type', 'text/plain; charset=UTF-8′)
self.end_headers()
self.wfile.write(self.path)
class WebServer(ThreadingTCPServer, BaseHTTPServer.HTTPServer):
pass
if __name__ == "__main__":
httpd = WebServer((‘127.0.0.1', 8000), EchoHandler)
httpd.serve_forever()
이 서버를 pywin32를 써서, Windows NT Service로 만들었다. 이건 처음 해보는 거라 아주 제대로 삽질했다. 일단 코드 자체는 아래처럼 생겼다. 대략 코드를 보고 동작을 이해할 수 있다 – 다만 개인적으로 이해 안가는 건, SERVICE_STOPPED
를 통보하면 없는 HANDLE이라는 이벤트 로그가 남더라. 그냥 놔두면 잘 된다.
import win32service, win32serviceutil, win32event, servicemanager
import httpd
class HttpdService(win32serviceutil.ServiceFramework):
_svc_name_ = 'httpd'
_svc_display_name_ = 'Simple Web Server'
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.haltEvent = win32event.CreateEvent(None, , , None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.server.shutdown()
win32event.SetEvent(self.haltEvent)
def SvcDoRun(self):
self.server = httpd.WebServer((‘127.0.0.1', 8000), httpd.EchoHandler)
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, "))
self.server.serve_forever()
win32event.WaitForSingleObject(self.haltEvent, win32event.INFINITE)
if __name__ == '__main__':
win32serviceutil.HandleCommandLine(HttpdService)
그리고 나서, py2exe
를 써서 다시 배포 가능한 binary로 만들었다.1 그리고 실제 서버로 가져가서 배포하고, 띄워서 이 삽질은 종료. py2exe
로 단순 바이너리는 몇 번 만들어봐서 쉽게 될 줄 알았는데, 서비스의 경우 빌드 방법이 좀 다르더라;
이건 python 설치 디렉터리의 lib/site-packages/py2exe/samples/advanced/setup.py
를 참고해서 아래처럼 하니까 잘 되더라.
# code from libsite-packagespy2exesamplesadvancedsetup.py
from distutils.core import setup
import py2exe
class Target:
def __init__(self, kw):
self.__dict__.update(kw)
self.version = "0.1.0.0″
self.company_name = "UPnL"
self.copyright = "Copyright (C) 2010 rein@????.org"
self.name = "Simple Echo WebServer"
target = Target(
description = "A sample web server service",
modules=[‘httpdservice'],
cmdline_style=‘pywin32',
)
setup(service = [target])
-
물론 한 움큼의 dll, pyd(=dll), .zip 파일이 따라 붙는다. ↩︎