[Python] Run FastAPI in a Windows Service

Discussão em 'Python' iniciado por Stack, Abril 23, 2025.

  1. Stack

    Stack Membro Participativo

    i have a big problem. I try to run FastAPI as a Windows Service after i converted the scripts in .exe with pyinstaller.

    This is my Code in apy.py:

    from fastapi import FastAPI, UploadFile, File, HTTPException, Request, Depends
    from fastapi.responses import JSONResponse
    from pydantic import BaseModel, Field
    import pandas as pd
    import io
    import numpy as np
    import logging
    from logging.handlers import RotatingFileHandler
    import sys
    import os
    import joblib
    from typing import Optional
    import json
    from fastapi.security import APIKeyHeader
    import string
    import datetime
    import uvicorn



    ############################################################################
    ################################# Start ####################################
    ############################################################################


    TicketClassifierPath = os.getenv("TicketClassifierPath")
    log_dir = os.path.join(TicketClassifierPath, "logs")
    if not os.path.exists(log_dir):
    os.makedirs(log_dir)

    # Logging einrichten
    log_file = os.path.join(log_dir, "api.log")
    # RotatingFileHandler konfigurieren
    handler = RotatingFileHandler(
    log_file,
    maxBytes=5 * 1024 * 1024, # 5 MB
    backupCount=3 # Anzahl der alten Logfiles, die behalten werden (server.log.1, server.log.2, ...)
    )

    # Logging-Format
    formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
    handler.setFormatter(formatter)

    # Root-Logger konfigurieren
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    logger.addHandler(handler)

    # FastAPI-App starten
    app = FastAPI()

    @app.get("/tst")
    def apikey():
    return {"Hello World"}

    def start_api():
    logging.info("Start uvicorn")
    uvicorn.run(app, host="0.0.0.0", port=8000, log_config=None, reload=False)

    if __name__ == '__main__':
    start_api()
    # Starte den API-Server mit: uvicorn api:app --reload
    # Im LAN auf allen Adressen erreichbar mit: uvicorn api:app --host 0.0.0.0 --port 8000 --reload


    This is my code in service.py:

    import os
    import logging
    from logging.handlers import RotatingFileHandler
    import sys
    import win32serviceutil
    import win32service
    import win32event
    import subprocess
    import servicemanager
    import socket
    from pydantic import BaseModel
    import json
    import time
    import api as api_server
    import threading

    # Unterverzeichnis für Logs erstellen
    TicketClassifierPath = os.getenv("TicketClassifierPath")
    log_dir = os.path.join(TicketClassifierPath, "logs")
    if not os.path.exists(log_dir):
    os.makedirs(log_dir)

    # Logging einrichten
    log_file = os.path.join(log_dir, "service.log")

    # RotatingFileHandler konfigurieren
    handler = RotatingFileHandler(
    log_file,
    maxBytes=5 * 1024 * 1024, # 5 MB
    backupCount=3 # Anzahl der alten Logfiles, die behalten werden (server.log.1, server.log.2, ...)
    )

    # Logging-Format
    formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
    handler.setFormatter(formatter)

    # Root-Logger konfigurieren
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    logger.addHandler(handler)

    class AppServerSvc (win32serviceutil.ServiceFramework):
    _svc_name_ = "TicketClassifierService"
    _svc_display_name_ = "Ticket Classifier API Service"
    _svc_description_ = "Dienst für die API des Ticket Classifiers"


    def __init__(self,args):
    win32serviceutil.ServiceFramework.__init__(self,args)
    self.hWaitStop = win32event.CreateEvent(None,0,0,None)
    socket.setdefaulttimeout(60)

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

    logging.info("Stop requested by service manager.")

    if self.process and self.process.poll() is None:
    logging.info("Terminating subprocess...")
    self.process.terminate()
    try:
    self.process.wait(timeout=10)
    logging.info("Subprocess terminated gracefully.")
    except subprocess.TimeoutExpired:
    logging.warning("Subprocess did not terminate in time. Killing it.")
    self.process.kill()
    logging.info("Subprocess killed.")

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

    def main(self): #Befindet sich hier im Python Pfad

    logging.info("Start Service...")

    python_path = os.getenv("python", "C:\\Program Files\\Python313\\python.exe")
    cmd = [python_path, "service.py", "run"]
    TicketClassifierPath = "C:\\Users\\orca\\Desktop\\TicketKIFull\\Ticket KI Small"

    ######################### For Run as a Exe #########################
    #cmd = ["TicketClassifier.exe", "run"]
    ####################################################################
    try:
    logging.info(f"Starte Uvicorn-Prozess: {' '.join(cmd)}")
    self.process = subprocess.Popen(cmd, cwd=TicketClassifierPath)
    logging.info("Subprozess gestartet.")
    except Exception as e:
    logging.critical(f"Fehler beim Start des Subprozesses: {e}")
    return

    rc = None
    while rc != win32event.WAIT_OBJECT_0:
    rc = win32event.WaitForSingleObject(self.hWaitStop, 10 * 1000)
    logging.info("Service is alive...")

    logging.info("Service wird gestoppt...")
    if self.process and self.process.poll() is None:
    logging.info("Terminiere Uvicorn-Prozess...")
    self.process.terminate()
    try:
    self.process.wait(timeout=10)
    logging.info("Prozess erfolgreich gestoppt.")
    except subprocess.TimeoutExpired:
    logging.warning("Prozess reagiert nicht, erzwinge Kill.")
    self.process.kill()
    logging.info("Prozess wurde gekillt.")


    if __name__ == '__main__':
    if len(sys.argv) > 1 and sys.argv[1] == "run":
    api_server.start_api()
    else:
    win32serviceutil.HandleCommandLine(AppServerSvc)


    I tested the following:

    • Run api.py manualy "python api.py" -> Works!
    • Run service.py manualy "python service.py run" -> Works!
    • Create Service with service.py and run that service -> Works! "python service.py install" "python service.py start"
    • Create Exe with pyinstaller and Run that -> Works! uncomment in script: "cmd = ["TicketClassifier.exe", "run"]" Create exe with pyinstaller: "pyinstaller --onfile --console --name TicketClassifier --hidden-import=win32timezone service.py" Run the exe File: "TicketClassifier.exe run"
    • Create Service with TicketClassifier.exe and run that service -> Doesn´t work!... "TicketClassifier.exe install" "TicketClassifier.exe start" Error 1053 at Start. But why it works before i converted it with pyinstaller?

    Anyone have a idea?

    Continue reading...

Compartilhe esta Página