python - error: no se puede iniciar un nuevo hilo
django multithreading (5)
Tengo un sitio que se ejecuta con la siguiente configuración:
Django + mod-wsgi + apache
En una de las solicitudes del usuario, envío otra solicitud HTTP a otro servicio y la soluciono 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 úsala 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 alguna vez empiezo a atrapar esta excepción:
error: can''t start new thread
en la línea de comenzar un nuevo hilo:
getting.start()
y la siguiente y la última línea de traceback es
File "/usr/lib/python2.5/threading.py", line 440, in start
_start_new_thread(self.__bootstrap, ())
Y la respuesta es: ¿Qué pasa?
Gracias a todos, y perdón por mi inglés puro. :)
Creo que la mejor manera en su caso es establecer el tiempo de espera del socket en lugar del hilo de desove:
h = httplib.HTTPSConnection(self.config[''server''],
timeout=self.config[''timeout''])
También puede establecer el tiempo de espera global predeterminado con la función socket.setdefaulttimeout()
.
Actualización : vea las respuestas a ¿Hay alguna manera de matar un hilo en Python? pregunta (hay varias bastante informativas) para entender por qué. Thread.__stop()
no finaliza el subproceso, sino que establece el marcador interno para que se considere ya detenido.
El error "no se puede iniciar el nuevo subproceso" es 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, se rechaza la solicitud de crear un nuevo subproceso.
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 su 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.
Está iniciando más hilos de los que su sistema puede manejar. Hay un límite en la cantidad de hilos que pueden estar activos para un proceso.
Su aplicación está iniciando los hilos más rápido de lo que los hilos 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.
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 como eso.
Y lo estoy probando ahora. Gracias a todos por la ayuda.
Si está intentando establecer el tiempo de espera, ¿por qué no usa urllib2 ?