最近使用django+requests+eventlet做了个小程序,eventlet用来替代原生的多线程,最后发现有关manage.py的功能全都不能用了,报错信息类似:

1
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来看哪些模块被修改了:

1
2
3
4
5
import eventlet
from eventlet.patcher import already_patched
print already_patched,1
eventlet.monkey_patch()
print already_patched,2

结果如下:

1
2
1
{'thread': True, 'os': True, 'socket': True, 'select': True, 'time': True} 2

可以看出,monkey_patch对上面模块进行了修改。

根据文档,如果仅想给一个模块打补丁,则可以使用import_patched()函数,所以写出下面的代码:

1
2
3
4
5
6
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

可是结果并不是想像中的样子:

1
2
False
{}

什么鬼!?是我打开的方式不对?换成系统库,把requests换成os结果居然也是一样的,换言之——用import_patched()函数并没生效!?其实并不是这样的,我们可以看is_monkey_patched函数代码如下:

1
2
3
4
5
6
7
8
9
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这个模块我们可以这样:

1
2
3
import requests
r = requests.get("xxxx")
print r.content

上面的3行代码可以毫无警告的正常执行,而:

1
2
3
4
import eventlet
requests = eventlet.patcher.import_patched('requests')
r = requests.get('xxx')
print r.content

虽然也会正常执行,但是会产生一个警告:

1
RuntimeWarning: Parent module 'requests' not found while handling absolute import from netrc import netrc, NetrcParseError

如果和我一样有强迫症的话,可以改成
requests = eventlet.patcher.import_patched('requests.__init__')
就不会有警告了。