次の設定で実行するサイトがあります。
Django + mod-wsgi + Apache
ユーザーのリクエストの1つで、別のHTTPリクエストを別のサービスに送信し、これをpythonのhttplibライブラリで解決します。
ただし、このサービスでは応答が長すぎず、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()
そして、トレースバックの次の最後の行は
File "/usr/lib/python2.5/threading.py", line 440, in start
_start_new_thread(self.__bootstrap, ())
そして答えは:何が起こるのですか?
みなさん、ありがとうございます。私の純粋な英語をごめんなさい。 :)
「新しいスレッドを開始できません」というエラーは、pythonプロセス内ですでに実行されているスレッドが多すぎるという事実と、何らかのリクエストのリソース制限が原因です。新しいスレッドを作成することは拒否されます。
おそらく、作成しているスレッドの数を確認する必要があります。作成できる最大数は環境によって決まりますが、少なくとも数百程度にする必要があります。
ここでアーキテクチャを再考することをお勧めします。とにかくこれが非同期で実行されているので、すべての要求に対して常にスレッドを起動するのではなく、スレッドのプールを使用して別のサイトからリソースをフェッチすることができます。
考慮すべきもう1つの改善点は、Thread.joinとThread.stopの使用です。これはおそらく、HTTPSConnectionのコンストラクターにタイムアウト値を提供することでより適切に達成されます。
システムで処理できる以上のスレッドを開始しています。 1つのプロセスでアクティブにできるスレッドの数には制限があります。
アプリケーションは、スレッドが完全に実行されているよりも速くスレッドを開始しています。多くのスレッドを開始する必要がある場合は、より制御された方法で開始する必要があります。スレッドプールを使用することをお勧めします。
あなたの場合の最善の方法は、スレッドを生成する代わりにソケットのタイムアウトを設定することだと思います:
_h = httplib.HTTPSConnection(self.config['server'],
timeout=self.config['timeout'])
_
socket.setdefaulttimeout()
関数を使用して、グローバルなデフォルトタイムアウトを設定することもできます。
更新:回答を参照 Pythonでスレッドを強制終了する方法はありますか? 質問(いくつかの非常に有益な情報があります)理由を理解してください。 Thread.__stop()
はスレッドを終了せず、内部フラグを設定してすでに停止していると見なします。
コードを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()
そんな感じ。
そして私は今それをテストしています。助けてくれてありがとう。
タイムアウトを設定しようとしている場合は、なぜ rllib2 を使用しないでください。
ThreadPoolExecutorを使用している場合は、max_workersがOSで許可されているスレッドよりも高いことが問題である可能性があります。
スレッドがすでに完了している場合でも、executorは最後に実行されたスレッドの情報をプロセステーブルに保持しているようです。つまり、アプリケーションが長期間実行されている場合、最終的にはThreadPoolExecutor.max_workersと同じ数のスレッドがプロセステーブルに登録されます。