2009-12-02 2 views
15

У меня есть сайт, который работает с конфигурацией наблюдения:ошибка: не может начать новую нить

Django + моды-WSGI + апачом

В одном из запроса пользователя, я отправить еще один запрос HTTP на другую службу , и решить это с помощью библиотеки httplib python.

Но иногда эта служба не получает ответа слишком долго, а тайм-аут для httplib не работает. Поэтому я создаю поток, в этом потоке я отправляю запрос на обслуживание и присоединяюсь к нему через 20 секунд (20 секунд - это время ожидания запроса). Вот как это работает:

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 

что-то вроде этого ...

И использовать его с помощью этой функции:

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

И все работает отлично, но когда я начинаю ловить это исключение:

error: can't start new thread 

на линии старта нового потока:

getting.start() 

и следующий, и последняя строка TRACEBACK является

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

И ответ: Что же случилось?

Спасибо за все, и извините за мой чистый английский. :)

ответ

5

Вы начинаете больше потоков, чем может обрабатывать ваша система. Существует ограничение на количество потоков, которые могут быть активны для одного процесса.

Ваше приложение запускает потоки быстрее, чем потоки выполняются до завершения. Если вам нужно запустить много потоков, вам нужно сделать это более контролируемым образом, я бы предложил использовать пул потоков.

20

Ошибка «не запускать новую тему» ​​почти наверняка из-за того, что у вас уже слишком много потоков, запущенных в вашем процессе python, и из-за какого-то ограниченного ресурса запрос на создание нового потока отказывается.

Возможно, вам стоит посмотреть количество тем, которые вы создаете; максимальное число, которое вы сможете создать, будет определяться вашей средой, но оно должно быть как минимум сотен.

Возможно, было бы неплохо переосмыслить вашу архитектуру здесь; так как это работает асинхронно, возможно, вы можете использовать пул потоков для извлечения ресурсов с другого сайта, а не для запуска потока для каждого запроса.

Еще одно улучшение, которое следует учитывать, это использование Thread.join и Thread.stop; это, вероятно, было бы лучше достигнуто путем предоставления значения тайм-аута конструктору HTTPSConnection.

+4

Обратите внимание, что количество запущенных потоков может быть показано с помощью 'threading.active_count()'. – 101

+0

Полезный sugestion, спасибо! –

4

Я думаю, что лучший способ в вашем случае, чтобы установить тайм-аут сокета вместо нерестового нити:

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

Также вы можете установить глобальный тайм-аут по умолчанию с socket.setdefaulttimeout() функции.

Обновление: См. Ответы на вопрос Is there any way to kill a Thread in Python? (есть несколько довольно информативных), чтобы понять, почему. Thread.__stop() не завершает поток, а устанавливает внутренний флаг, так что считается, что он уже остановлен.

+0

Это может быть полезно для меня. Спасибо. – Oduvan

3

Если вы связываете, чтобы установить таймаут, почему бы вам не использовать urllib2.

+0

urllib2 не имеет времени поиска. – Oduvan

+1

urllib2 действительно есть тайм-аут. urllib2.urlopen (url [, data] [, timeout]) Prashanth

+1

аргумент 'timeout' является новым в Python 2.6 –

4

Я полностью переписываю код с httplib на 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() 

что-то в этом духе.

И я тестирую его сейчас. Спасибо всем вам за помощь.

Смежные вопросы