2009-12-02 13 views
15

Tengo un sitio que se ejecuta con la configuración siguiente:error: No se puede iniciar un nuevo hilo

Django + mod-WSGI + Apache

En uno de petición del usuario, que enviar otra solicitud HTTP a otro servicio y resuelve esto mediante la biblioteca httplib de python.

Pero a veces este servicio no recibe una respuesta demasiado larga, y el tiempo de espera para httplib no funciona. Así que estoy creando un hilo, en este hilo envío una solicitud al servicio, y me uno a él después de 20 segundos (20 segundos - es un tiempo de espera de solicitud). Así es como funciona:

class HttpGetTimeOut(threading.Thread): 
    def __init__(self,**kwargs): 
     self.config = kwargs 
     self.resp_data = None 
     self.exception = None 
     super(HttpGetTimeOut,self).__init__() 
    def run(self): 

     h = httplib.HTTPSConnection(self.config['server']) 
     h.connect() 
     sended_data = self.config['sended_data'] 
     h.putrequest("POST", self.config['path']) 
     h.putheader("Content-Length", str(len(sended_data))) 
     h.putheader("Content-Type", 'text/xml; charset="utf-8"') 
     if 'base_auth' in self.config: 
      base64string = base64.encodestring('%s:%s' % self.config['base_auth'])[:-1] 
      h.putheader("Authorization", "Basic %s" % base64string) 
     h.endheaders() 

     try: 
      h.send(sended_data) 
      self.resp_data = h.getresponse() 
     except httplib.HTTPException,e: 
      self.exception = e 
     except Exception,e: 
      self.exception = e 

algo como esto ...

y usarlo por esta función:

getting = HttpGetTimeOut(**req_config) 
getting.start() 
getting.join(COOPERATION_TIMEOUT) 
if getting.isAlive(): #maybe need some block 
    getting._Thread__stop() 
    raise ValueError('Timeout') 
else: 
    if getting.resp_data: 
     r = getting.resp_data 
    else: 
     if getting.exception: 
      raise ValueError('REquest Exception') 
     else: 
      raise ValueError('Undefined exception') 

Y todo funciona bien, pero en algún momento que se inicia la captura de esta excepción:

error: can't start new thread 

en la línea de partida nuevo hilo:

getting.start() 

y la siguiente y la última línea de rastreo es

File "/usr/lib/python2.5/threading.py", line 440, in start 
    _start_new_thread(self.__bootstrap,()) 

Y la respuesta es: ¿Cuál es suceda?

Gracias a todos, y perdón por mi inglés puro. :)

Respuesta

5

Está iniciando más subprocesos de los que puede manejar su sistema. Hay un límite en la cantidad de hilos que pueden estar activos para un proceso.

Su aplicación está iniciando los subprocesos más rápido que los subprocesos se están ejecutando hasta su finalización. Si necesita iniciar muchos hilos que necesita para hacerlo de una manera más controlada, le sugiero que use un grupo de hilos.

20

El error "no se puede iniciar el nuevo subproceso" casi seguro debido al hecho de que ya tiene demasiados subprocesos ejecutándose dentro del proceso de python, y debido a un límite de recursos de algún tipo, la solicitud de crear un nuevo subproceso es rechazado

Probablemente deberías mirar la cantidad de hilos que estás creando; el número máximo que podrá crear estará determinado por su entorno, pero debería ser del orden de cientos como mínimo.

Probablemente sería una buena idea volver a pensar su arquitectura aquí; dado que esto se está ejecutando de manera asíncrona de todos modos, quizás podría usar un conjunto de hilos para buscar recursos desde otro sitio en lugar de siempre iniciar un hilo para cada solicitud.

Otra mejora a considerar es el uso de Thread.join y Thread.stop; esto probablemente se lograría mejor al proporcionar un valor de tiempo de espera al constructor de HTTPSConnection.

+4

Tenga en cuenta que la cantidad de subprocesos en ejecución se puede mostrar con 'threading.active_count()'. – 101

+0

Sugerencia útil, ¡gracias! –

4

creo que la mejor manera en su caso es poner el tiempo de socket en vez de hilo de desove:

h = httplib.HTTPSConnection(self.config['server'], 
          timeout=self.config['timeout']) 

También se puede ajustar el tiempo de espera predeterminado global con socket.setdefaulttimeout() función.

Actualización: Vea las respuestas a la pregunta Is there any way to kill a Thread in Python? (hay varias bastante informativas) para entender por qué. Thread.__stop() no termina el hilo, sino que establece el indicador interno para que se considere ya detenido.

+0

Puede ser útil para mí. Gracias. – Oduvan

3

Si está intentando establecer el tiempo de espera, ¿por qué no usa urllib2?

+0

urllib2 no tiene tiempo de espera de conexión. – Oduvan

+1

urllib2 tiene tiempo de espera. urllib2.urlopen (url [, data] [, timeout]) Prashanth

+1

El argumento 'timeout' es nuevo en Python 2.6 –

4

Reescribo completamente el código de httplib a pycurl.

c = pycurl.Curl() 
c.setopt(pycurl.FOLLOWLOCATION, 1) 
c.setopt(pycurl.MAXREDIRS, 5) 
c.setopt(pycurl.CONNECTTIMEOUT, CONNECTION_TIMEOUT) 
c.setopt(pycurl.TIMEOUT, COOPERATION_TIMEOUT) 
c.setopt(pycurl.NOSIGNAL, 1) 
c.setopt(pycurl.POST, 1) 
c.setopt(pycurl.SSL_VERIFYHOST, 0) 
c.setopt(pycurl.SSL_VERIFYPEER, 0) 
c.setopt(pycurl.URL, "https://"+server+path) 
c.setopt(pycurl.POSTFIELDS,sended_data) 

b = StringIO.StringIO() 
c.setopt(pycurl.WRITEFUNCTION, b.write) 

c.perform() 

algo así como eso.

Y lo estoy probando ahora. Gracias a todos por la ayuda.

Cuestiones relacionadas