2008-11-06 13 views
29

Uruchomiłem mój HTTPServer w osobnym wątku (za pomocą modułu wątków, który nie ma możliwości zatrzymania wątków ...) i chcę przestać wyświetlać żądania, gdy główny wątek również zostanie wyłączony.Jak zatrzymać BaseHTTPServer.serve_forever() w podklasie BaseHTTPRequestHandler?

dokumentacji Pythona stwierdza, że ​​BaseHTTPServer.HTTPServer jest podklasą SocketServer.TCPServer, który obsługuje metodę shutdown, ale brakuje w HTTPServer.

Cały moduł BaseHTTPServer ma bardzo mało dokumentacji :(

Odpowiedz

18

powinienem zacząć od stwierdzenia, że ​​„prawdopodobnie nie byłoby to zrobić sam, ale mam w przeszłości”. Na serve_forever (od SocketServer.py) metoda wygląda następująco:

def serve_forever(self): 
    """Handle one request at a time until doomsday.""" 
    while 1: 
     self.handle_request() 

można zastąpić (w podklasy) while 1 z while self.should_be_running i zmodyfikować tę wartość z innego wątku coś takiego:.

def stop_serving_forever(self): 
    """Stop handling requests""" 
    self.should_be_running = 0 
    # Make a fake request to the server, to really force it to stop. 
    # Otherwise it will just stop on the next request. 
    # (Exercise for the reader.) 
    self.make_a_fake_request_to_myself() 

Edit: I wykopali rzeczywisty kod użyłem w czasie:

class StoppableRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer): 

    stopped = False 
    allow_reuse_address = True 

    def __init__(self, *args, **kw): 
     SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, *args, **kw) 
     self.register_function(lambda: 'OK', 'ping') 

    def serve_forever(self): 
     while not self.stopped: 
      self.handle_request() 

    def force_stop(self): 
     self.server_close() 
     self.stopped = True 
     self.create_dummy_request() 

    def create_dummy_request(self): 
     server = xmlrpclib.Server('http://%s:%s' % self.server_address) 
     server.ping() 
+0

myślę możesz wywołać "self.serve_forever()" po "self.stopped = True" i unikać implementacji "create_dummy_request (self)". Dla mnie to zadziałało, ale może istnieć subtelność, której mi brakuje. –

+1

Jeśli chodzi o źródła 'BaseServer.serve_forever()' w Pythonie 2.7, widzę, że ma już zaimplementowaną flagę, '__shutdown_request', plus' __is_shut_down' 'threading.Event' na górze. Tak więc rzeczywistą odpowiedzią, którą tu widzę, jest linia 'allow_reuse_address = True'. –

18

W mojej instalacji Python 2.6, mogę nazwać na bazowym tcpserver - to nadal istnieje wewnątrz HTTPServer:

TCPServer.shutdown 


>>> import BaseHTTPServer 
>>> h=BaseHTTPServer.HTTPServer(('',5555), BaseHTTPServer.BaseHTTPRequestHandler) 
>>> h.shutdown 
<bound method HTTPServer.shutdown of <BaseHTTPServer.HTTPServer instance at 0x0100D800>> 
>>> 
+0

ah. tak, a ja * czytam * dokumentację 2.6 - niestety, mam 2.5. Bummer. Mnie idioto. –

+4

Należy pamiętać, że nawet po wyłączeniu gniazdo nadal nasłuchuje na interfejsie/porcie (testowane w Pythonie v2.7). Powinieneś również wywołać: 'h.socket.close()'. –

15

myślę, że można użyć [serverName].socket.close()

+2

To był jedyny sposób, który działał dla mnie, próbowałem przerywać z while while some_flag, z server.shutdown(). Tylko socket.close() osiągnął pożądane. –

+1

Miałem ten sam problem. Wydaje mi się, że moim problemem było to, że próbowałem wywołać metodę server.shutdown() z wewnętrznego programu obsługi, a ta sekwencja spowodowała zakleszczenie, ponieważ podejrzewam, że serwer czeka na zakończenie programu obsługi przed powrotem z wywołania shutdown(). Wadą tego jest to, że na stdoutie pojawia się przerażająco wyglądający wyjątek, który jakoś chciałbym stłumić. –

+0

Próbowałem tego, ale rzuca wyjątek. Najnowsza wersja ma wyeksportowaną funkcję shutdown(). – Feru

10

Innym sposobem, aby to zrobić, w oparciu o http://docs.python.org/2/library/basehttpserver.html#more-examples, brzmi: zamiast serve_forever(), zachować służąc dopóki warunek jest spełniony, z serwerem sprawdzanie stanu przed i po każdym żądaniu. Na przykład:

import CGIHTTPServer 
import BaseHTTPServer 

KEEP_RUNNING = True 

def keep_running(): 
    return KEEP_RUNNING 

class Handler(CGIHTTPServer.CGIHTTPRequestHandler): 
    cgi_directories = ["/cgi-bin"] 

httpd = BaseHTTPServer.HTTPServer(("", 8000), Handler) 

while keep_running(): 
    httpd.handle_request() 
+0

Po zobaczeniu wszystkich innych hack tu i tam, jest to rzadka odpowiedź, która opiera się wyłącznie na oficjalnie udokumentowanym zachowaniu, dlatego jest uważana za kanoniczną. Wcześniej głosowane! – RayLuo

9

W Pythonie 2.7, nazywając shutdown() działa, ale tylko jeśli służą pomocą serve_forever, ponieważ wykorzystuje asynchroniczny wybrać i pętla odpytywania. Uruchomienie własnej pętli za pomocą handle_request() ironicznie wyklucza tę funkcjonalność, ponieważ implikuje nieme wywołanie blokujące.

Od SocketServer.py za BaseServer:

def serve_forever(self, poll_interval=0.5): 
    """Handle one request at a time until shutdown. 

    Polls for shutdown every poll_interval seconds. Ignores 
    self.timeout. If you need to do periodic tasks, do them in 
    another thread. 
    """ 
    self.__is_shut_down.clear() 
    try: 
     while not self.__shutdown_request: 
      # XXX: Consider using another file descriptor or 
      # connecting to the socket to wake this up instead of 
      # polling. Polling reduces our responsiveness to a 
      # shutdown request and wastes cpu at all other times. 
      r, w, e = select.select([self], [], [], poll_interval) 
      if self in r: 
       self._handle_request_noblock() 
    finally: 
     self.__shutdown_request = False 
     self.__is_shut_down.set() 

Herezje część mojego kodu dla robią wyłączenie blokady z innego wątku, używając zdarzenie czekać na zakończenie:

class MockWebServerFixture(object): 
    def start_webserver(self): 
     """ 
     start the web server on a new thread 
     """ 
     self._webserver_died = threading.Event() 
     self._webserver_thread = threading.Thread(
       target=self._run_webserver_thread) 
     self._webserver_thread.start() 

    def _run_webserver_thread(self): 
     self.webserver.serve_forever() 
     self._webserver_died.set() 

    def _kill_webserver(self): 
     if not self._webserver_thread: 
      return 

     self.webserver.shutdown() 

     # wait for thread to die for a bit, then give up raising an exception. 
     if not self._webserver_died.wait(5): 
      raise ValueError("couldn't kill webserver") 
+0

To jest poprawna odpowiedź! To rozwiązało dla mnie i to jest łatwiejsze niż jakiekolwiek inne rzeczy tutaj! Dzięki! – Elric

0

Próbowałem wszystkie powyższe Możliwe rozwiązanie i skończyło się na problemie "kiedyś" - jakoś tak naprawdę go nie robiłem - skończyło się więc robiącym brudne rozwiązanie, które cały czas działało dla mnie:

Jeśli wszystko powyższe zawiedzie, wtedy brute force zabić wątek za pomocą czegoś takiego:

import subprocess 
cmdkill = "kill $(ps aux|grep '<name of your thread> true'|grep -v 'grep'|awk '{print $2}') 2> /dev/null" 
subprocess.Popen(cmdkill, stdout=subprocess.PIPE, shell=True) 
+1

'server.socket.close()' zawsze wydaje się działać: –

5

zdarzenie pętle kończy SIGTERM, Ctrl + C lub gdy shutdown() nazywa.

server_close() musi zostać wywołany po server_forever(), aby zamknąć gniazdo nasłuchu.

import http.server 

class StoppableHTTPServer(http.server.HTTPServer): 
    def run(self): 
     try: 
      self.serve_forever() 
     except KeyboardInterrupt: 
      pass 
     finally: 
      # Clean-up server (close socket, etc.) 
      self.server_close() 

Prosty serwer stoppable z działania użytkownika (SIGTERM, Ctrl + C, ...):

server = StoppableHTTPServer(("127.0.0.1", 8080), 
          http.server.BaseHTTPRequestHandler) 
server.run() 

Server uruchomiony w wątku:

import threading 

server = StoppableHTTPServer(("127.0.0.1", 8080), 
          http.server.BaseHTTPRequestHandler) 

# Start processing requests 
thread = threading.Thread(None, server.run) 
thread.start() 

# ... do things ... 

# Shutdown server 
server.shutdown() 
thread.join() 
Powiązane problemy