2008-11-06 7 views
29

Estoy ejecutando mi HTTPServer en una secuencia separada (utilizando el módulo de subprocesamiento que no tiene manera de detener subprocesos ...) y quiero dejar de atender solicitudes cuando el hilo principal también se apaga.Cómo detener BaseHTTPServer.serve_forever() en una subclase BaseHTTPRequestHandler?

documentación La Python afirma que BaseHTTPServer.HTTPServer es una subclase de SocketServer.TCPServer, que apoya un método shutdown, pero no se encuentra en HTTPServer.

todo el módulo BaseHTTPServer tiene muy poca documentación :(

Respuesta

18

que debería empezar diciendo que "probablemente no haría esto a mí mismo, pero tengo en el pasado". El serve_forever (de SocketServer.py) método es el siguiente:

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

podría reemplazar (en la subclase) while 1 con while self.should_be_running, y modificar ese valor de un hilo diferente Algo así como:.

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: desenterrado el código real utilicé en ese momento:

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

pienso puede llamar a "self.serve_forever()" después de "self.stopped = True" y evitar implementar "create_dummy_request (self)". Me ha funcionado hasta ahora, pero puede haber una sutileza que me falta. –

+1

En cuanto a fuentes 'BaseServer.serve_forever()' en Python 2.7, veo que ya tiene implementado un indicador, '__shutdown_request', más' __is_shut_down' 'threading.Event' en la parte superior. Entonces, la respuesta real que veo aquí es la línea 'allow_reuse_address = True'. –

18

En mi instalación de Python 2.6, se puede llamar así en la TCPServer subyacente - que todavía existe dentro de su 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. sí, y yo * estoy * leyendo los documentos 2.6 - ¡ay !, estoy ejecutando 2.5. Gorrón. Yo idiota. –

+4

Tenga en cuenta que incluso después del apagado, el socket seguirá escuchando en la interfaz/puerto (probado en Python v2.7). También debe llamar: 'h.socket.close()'. –

15

creo que se puede utilizar [serverName].socket.close()

+2

Esta fue la única forma que funcionó para mí, intenté interrumpir con while_number_flag con server.shutdown(). Solo socket.close() logró lo deseado. –

+1

Tuve el mismo problema. Creo que mi problema era que estaba intentando llamar a server.shutdown() desde el manejador interno y esa secuencia resulta en un punto muerto ya que sospecho que el servidor está esperando que el controlador complete antes de regresar de la llamada shutdown(). La desventaja de esto es que tengo una excepción de aspecto aterrador impresa en stdout que me gustaría suprimir de alguna manera. –

+0

Lo he intentado pero arroja una excepción. versión reciente tiene la función shutdown() exportada. – Feru

10

Otra manera de hacerlo, basado en http://docs.python.org/2/library/basehttpserver.html#more-examples, es: en lugar de serve_forever(), que sirve a mantener, siempre y cuando se cumpla una condición, con el servidor verificando la condición antes y después de cada solicitud. Por ejemplo:

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

Después de ver todos los demás hacks aquí y allá, esta es una respuesta rara que solo se basa en el comportamiento oficialmente documentado, por lo tanto, se considera canónica. Up-votado! – RayLuo

9

en Python 2.7, llamar a shutdown() funciona, pero sólo si usted está sirviendo a través de serve_forever, ya que utiliza asíncrono y seleccionar un bucle de sondeo. Ejecutar su propio bucle con handle_request() irónicamente excluye esta funcionalidad porque implica una llamada de bloqueo tonta.

De BaseServer de SocketServer.py:

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() 

Aquí está parte de mi código para hacer un cierre de bloqueo desde otro hilo, usando un evento que esperar a la finalización:

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

¡Esta es la respuesta correcta! ¡Esto se resolvió para mí y es más fácil que cualquier otra cosa aquí! ¡Gracias! – Elric

0

me trataron todos por encima posible solución y terminó teniendo un problema de "alguna vez" - de alguna manera realmente no lo hizo - así que terminé haciendo una solución sucia que funcionó todo el tiempo para mí:

Si todo lo anterior falla, entonces la fuerza bruta matar el hilo usando algo como esto:

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()' siempre parece funcionar: –

5

El evento de bucles extremos en SIGTERM, Ctrl +Cshutdown() o cuando se llama.

server_close() debe llamarse después de server_forever() para cerrar la toma de audición.

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() 

servidor simple con imparable acción del usuario (SIGTERM, Ctrl +C, ...):

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

Server que se ejecuta en un hilo:

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() 
Cuestiones relacionadas