Odu*_*van 23 python django multithreading mod-wsgi httplib
我有一个运行跟随配置的网站:
Django + mod-wsgi + apache
在用户的一个请求中,我向另一个服务发送另一个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
Run Code Online (Sandbox Code Playgroud)
像这样的东西......
并通过此功能使用它:
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')
Run Code Online (Sandbox Code Playgroud)
一切正常,但有时我开始捕捉这个异常:
error: can't start new thread
Run Code Online (Sandbox Code Playgroud)
在启动新线程的行:
getting.start()
Run Code Online (Sandbox Code Playgroud)
追溯的下一步和最后一行是
File "/usr/lib/python2.5/threading.py", line 440, in start
_start_new_thread(self.__bootstrap, ())
Run Code Online (Sandbox Code Playgroud)
答案是:发生了什么?
感谢所有人,对不起我的纯英语.:)
Gab*_*eid 29
"无法启动新线程"错误几乎可以肯定,因为您已经在python进程中运行了太多线程,并且由于某种资源限制,创建新线程的请求被拒绝.
您应该查看您正在创建的线程数; 您将能够创建的最大数量将由您的环境决定,但它至少应为数百个.
在这里重新思考你的架构可能是一个好主意; 因为这无论如何都是异步运行,也许你可以使用一个线程池从另一个站点获取资源,而不是总是为每个请求启动一个线程.
要考虑的另一个改进是使用Thread.join和Thread.stop; 通过为HTTPSConnection的构造函数提供超时值,可能会更好.
您启动的线程数多于系统可以处理的线程数.对于一个进程可以处于活动状态的线程数有限制.
您的应用程序启动线程的速度比线程运行完成的速度快.如果您需要启动多个线程,您需要以更加可控的方式执行它,我建议使用线程池.
我认为在你的情况下最好的方法是设置套接字超时而不是产生线程:
h = httplib.HTTPSConnection(self.config['server'],
timeout=self.config['timeout'])
Run Code Online (Sandbox Code Playgroud)
您还可以使用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()
Run Code Online (Sandbox Code Playgroud)
类似的东西。
我现在测试它。谢谢大家的帮助。
小智 5
我在类似的情况下运行,但我的进程需要运行大量线程来处理大量连接。
我用命令计算了线程数:
ps -fLu user | wc -l
它显示了 4098。
我切换到用户并查看系统限制:
sudo -u myuser -s /bin/bash
ulimit -u
得到 4096 作为响应。
所以,我编辑了 /etc/security/limits.d/30-myuser.conf 并添加了以下几行:
myuser hard nproc 16384
myuser soft nproc 16384
重新启动服务,现在它以 7017 个线程运行。
附言。我有一个 32 核服务器,我正在使用这种配置处理 18k 并发连接。