eventlet,django,requests问题杂记
最近使用django+requests+eventlet做了个小程序,eventlet用来替代原生的多线程,最后发现有关manage.py的功能全都不能用了,报错信息类似:
django.db.utils.DatabaseError: DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 139911009593152 and this is thread id 51055504.
产生这个问题的原因在于我的monky_patch()是在爬虫模块中执行,而希望这个爬虫模块能够保持独立性不希望和django融合的太深,所以解决问题只需根据实际需求在manage.py或settings.py或wsgi.py中先把monkey_patch()执行了即可。
这个问题解决后,我打开了eventlet的源码目录,发现monkey_patch()函数本质上仅仅对一些系统库进行了修改,比如我们可以输出already_patched来看哪些模块被修改了:
import eventlet
from eventlet.patcher import already_patched
print already_patched,1
eventlet.monkey_patch()
print already_patched,2
结果如下:
1
{'thread': True, 'os': True, 'socket': True, 'select': True, 'time': True} 2
可以看出,monkey_patch对上面模块进行了修改。
根据文档,如果仅想给一个模块打补丁,则可以使用import_patched()函数,所以写出下面的代码:
import eventlet
from eventlet.patcher import is_monkey_patched
from eventlet.patcher import already_patched
requests = eventlet.import_patched('requests')
print is_monkey_patched('requests')
print already_patched
可是结果并不是想像中的样子:
False
{}
什么鬼!?是我打开的方式不对?换成系统库,把requests换成os结果居然也是一样的,换言之——用import_patched()函数并没生效!?其实并不是这样的,我们可以看is_monkey_patched函数代码如下:
def is_monkey_patched(module):
"""Returns True if the given module is monkeypatched currently, False if
not. *module* can be either the module itself or its name.
Based entirely off the name of the module, so if you import a
module some other way than with the import keyword (including
import_patched), this might not be correct about that particular
module."""
return module in already_patched or \
getattr(module, '__name__', None) in already_patched
注释解释的很清楚,如果我们使用其他的方式import模块(包括inmport_pached)这个函数并不一定返回正确的值。关键就在于already_patched这个字典的值什么时候被改变了,通过源码发现这个字典只有在monkey_patch()函数中才会被修改,这也就解释了上面的疑问。那么怎么确定import_patched函数确实生效了呢?对于requests这个模块我们可以这样:
import requests
r = requests.get("xxxx")
print r.content
上面的3行代码可以毫无警告的正常执行,而:
import eventlet
requests = eventlet.patcher.import_patched('requests')
r = requests.get('xxx')
print r.content
虽然也会正常执行,但是会产生一个警告:
RuntimeWarning: Parent module 'requests' not found while handling absolute import from netrc import netrc, NetrcParseError
如果和我一样有强迫症的话,可以改成
requests = eventlet.patcher.import_patched('requests.__init__')
就不会有警告了。