想写一个分析日志的工具,用 asyncio 处理的时候,想大家帮忙看一下哪里有问题,实际处理日志的时候,readlines 的时候,一个 650m 的文件需要 3 秒多,然后往字典里面加的时候需要 8 秒多,所以一个文件要 12 秒多,
现在的效果是,多个文件消耗的时间就是 12*n,就是说并没有提升,不是说 await 的时候,会把当前执行的内容挂起,然后执行下一个任务么,可能是我哪里有问题,麻烦大家给看看,代码如下。
import asyncio
import re
import time
from pathlib import Path
ip_find = re.compile(r'((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))).){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))')
ip_database = dict()
async def ip_address(log):
"""分析 ip""" with open(log) as f: log_data = f.readlines() try: for log_ips in log_data: ip_search = ip_find.search(log_ips) if ip_search: ip_database[ip_search.group(0)] = ip_database.get(ip_search.group(0), 0) + 1 except Exception as e: print(e)
async def generator(log):
await ip_address(log)
if __name__ == '__main__':
path = Path(r"D:\anlysis_log") start = time() loop = asyncio.get_event_loop() tasks = [generator(x) for x in path.iterdir()] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
![]() | 1 111111111111 2018-12-02 20:02:03 +08:00 via Android asyncio 挂起的是 io 等待,可不包括正则啊 |
![]() | 2 meowoo OP @111111111111 log_data = f.readlines() 这个不是 io 等待么,读取文件啊,还是说要函数整体 |
![]() | 3 sunwei0325 2018-12-02 20:13:20 +08:00 用 aiofiles 或者将读取文件的操作放到线程池 /进程池里面 |
4 wwwjfy 2018-12-02 20:14:41 +08:00 - asyncio 的任务是显式的,有 await 才会切回到 event loop,不是在 function 前面加 async 就行。可以试试 https://github.com/Tinche/aiofiles - 这里的瓶颈在磁盘 IO,asyncio 作用应该不会太明显;作用更大的地方是多个 IO 并行执行的时候,这里在读磁盘的时候就去那里执行 CPU 操作,另一个任务可能在等网络请求返回 |
![]() | 5 AlisaDestiny 2018-12-02 20:19:20 +08:00 你没理解 1 楼的意思,他是说你的程序消耗的时间主要在正则查找这里,也就是这条语句:ip_search = ip_find.search(log_ips)。 |
![]() | 6 meowoo OP @sunwei0325 好的谢谢,我看下这个库 |
![]() | 7 meowoo OP @wwwjfy 我原来理解的是如果 await 的函数在坐 io 等待,就会切换 event loop,就是我在 ip_address 中等待读取文件,这个时间应该切换到下一个 event,等待读取后再继续操作,现在看来理解是有问题的,我去看看 aiofiles 库,多谢。 |
![]() | 8 meowoo OP @AlisaDestiny 但是上面文件读取也是在耗时啊,多少会有点儿提升的把 |
9 clearT 2018-12-02 23:34:16 +08:00 asyncio 好像不支持异步的读取文件,即使设置为非阻塞读取模式,所以是不会引起切换的。 [Asyncio Wiki]( https://github.com/python/asyncio/wiki/ThirdParty#filesystem) |
![]() | 11 congeec 2018-12-03 11:34:09 +08:00 |
![]() | 12 no1xsyzy 2018-12-03 14:08:48 +08:00 正则写错了一处, "." 表示匹配任何字符,而不是 ip 地址中的分隔点。 |
![]() | 13 meowoo OP @congeec 现在用 aiofiles 是异步读取的文件,用 async for 去异步执行正则匹配的时候,print 的代码也看到是异步的,但是超级慢,比去掉 async 快了 15 倍 以上 ,现在我晕了已经,不知道咋回事。 |
![]() | 16 no1xsyzy 2018-12-04 10:02:45 +08:00 @meowoo 那么你连 r 是什么意思都没明白,`r`是拒绝 Python 字符串转义啊,并不会影响正则的意思。 你要说 `r` 让 "." 不生效(具有正则表达式含义),那么 "\d" "(" ")" "{" "}" "[0-5]" 也不会生效啊。 >>> import re >>> re.compile(r'((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))).){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))').search("3|3|3| 3") <re.Match object; span=(0, 7), match='3|3|3|3'> |
![]() | 17 no1xsyzy 2018-12-04 10:08:53 +08:00 另外,不要做干净正则,做个脏正则就行了, ((?:\d{1,3}\.){3}\d{1,3}) 在反复出现没搜索到的情况(就是说 [不是] 一行单一个 IP 结束的情况) |
![]() | 19 meowoo OP @congeec 就是把读取文件改成 aiofiles, for 改成 async for 我看了 debug 之后发现,每次进入 async for 的时候,都会 wait,重新回到 await ip_address(log) 中,然后到 event loop 去看有没有其他等待的 event,然后导致每一行在循环的时候都去 await 一次,结果特别慢,我个人理解,不知道对不对,请指教,async for 不是这样用的么? 代码如下 `async def ip_address(log):` """分析 ip""" async with open(log) as f: try: async for log_ips in f: ip_search = ip_find.search(log_ips) if ip_search: ip_database[ip_search.group(0)] = ip_database.get(ip_search.group(0), 0) + 1 except Exception as e: print(e) |
![]() | 20 congeec 2018-12-05 03:05:21 +08:00 @meowoo 加 async 并不会让你的同步代码变成异步 好好了解一下异步风格里的”让出执行”权吧 看看下面的文章。划重点: asyncio.gather(), futures, ProcessPoolExecutor。理解 futures 你就知道怎么用同步风格写异步代码 https://blog.konpat.me/python-turn-sync-functions-to-async/ https://docs.python.org/3/library/asyncio-eventloop.html#id14 |