# 3.11. 框架完善--使用协程池实现异步以及并发控制
# 目标
- 掌握gevent中Pool基本使用
- 实现代码的重构,使用gevent来进一步提高效率
# 1. 为什么使用gevent
对于I/O密集型任务,gevent能对性能做很大提升的,协程的创建、调度开销都比线程小的多。
# 2. 通过配置文件设置属性,来判断所使用的异步方式
# 异步方式 thread、coroutine
ASYNC_TYPE = 'coroutine'
1
2
2
# 3. 让gevent的Pool和线程池Pool的接口一致
因此需要单独对gevent的Pool进行一下修改,具体如下: 在scrapy_plus下创建async包,随后创建coroutine.py模块
# scrapy_plus/async/coroutine.py
'''
由于gevent的Pool的没有close方法,也没有异常回调参数
引出需要对gevent的Pool进行一些处理,实现与线程池一样接口,实现线程和协程的无缝转换
'''
from gevent.pool import Pool as BasePool
import gevent.monkey
gevent.monkey.patch_all() # 打补丁,替换内置的模块
class Pool(BasePool):
'''协程池
使得具有close方法
使得apply_async方法具有和线程池一样的接口
'''
def apply_async(self, func, args=None, kwds=None, callback=None, error_callback=None):
return super().apply_async(func, args=args, kwds=kwds, callback=callback)
def close(self):
'''什么都不需要执行'''
pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4. 在引擎中使用上:
# scrapy_plus/core/engine.py
import time
import importlib
from datetime import datetime
from scrapy_plus.http.request import Request # 导入Request对象
from scrapy_plus.utils.log import logger # 导入logger
from scrapy_plus.conf import settings
# 判断使用什么异步模式,改用对应的异步池
if settings.ASYNC_TYPE == 'thread':
from multiprocessing.dummy import Pool # 导入线程池对象
elif settings.ASYNC_TYPE == 'coroutine':
from scrapy_plus.async.coroutine import Pool
else:
raise Exception("不支持的异步类型:%s, 只能是'thread'或者'coroutine'"%settings.ASYNC_TYPE)
# 注意:
# 由于打patch补丁是为了替换掉socket为非阻塞的
# 而下载器中正好使用了requests模块,如果在这之后导入协程池,会导致requests中使用的socket没有被替换成功
# 从而有可能导致使用出现问题
from .scheduler import Scheduler
from .downloader import Downloader
class Engine(object):
......
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 小结
- 本小结重点
- 掌握gevent.monkey_path的使用,注意其中的导入顺序
- 了解gevent.pool的使用
- 完成gevent版本的异步实现