0%

Chrome浏览器同一资源多次请求被串行化

问题

  • fastapi 中执行一段 cpu 密集型代码,在 Edge(Chromium 内核) 浏览器打开两个 tab,同时请求该接口,发现后一个请求会被阻塞,直到前一个请求完成。代码 demo 如下:
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
27
28
29
30
31
import asyncio
import time
from concurrent.futures import ProcessPoolExecutor
from datetime import datetime

import uvicorn
from fastapi import FastAPI
from fastapi.concurrency import run_in_threadpool

app = FastAPI()


def c():
# cpu heavy task cost 5s
t = time.perf_counter()
for i in range(10**8 * 5):
pass
print(time.perf_counter() - t)
return 1


@app.get("/f")
async def f():
print(datetime.now())
r = await run_in_threadpool(c)
return r

if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)


  • 已经使用了run_in_threadpool 将同步代码放到线程池中执行,避免阻塞事件循环。为什么还是会出现这个问题呢?一开始怀疑框架的线程池有问题,尝试换用多进程,问题依旧:
1
2
3
4
5
6
7
8
9
10
11
12
13
import asyncio
from concurrent.futures import ProcessPoolExecutor

#限制进程池大小,避免资源耗尽
pool = ProcessPoolExecutor(4)


@app.get("/f2")
async def f2():
print(datetime.now())
loop = asyncio.get_running_loop()
r = await loop.run_in_executor(pool, c)
return r
  • 继续怀疑框架问题,搜了一下 issue,发现没有人遇到过,尝试用flask,问题依然。此时意识到这可能是浏览器的问题,果然换用 Firefox 没有复现此问题。

原因

  • 提出一个好问题,等于问题解决了一半
  • 拿出祖传的 google 找到了 Chrome 浏览器缓存锁:同一资源(URL)被多次请求时,后续请求会被阻塞(最多 20s),直到第一个请求返回。
  • 一次 Chrome 缓存锁的有趣探索

解决

  • 给 URL 添加无用的参数(时间戳/随机数,避免被视为相同请求): http://127.0.0.1/url?r=5431
  • 浏览器打开->开发者工具->网络:禁用缓存
  • 换用 Firefox 浏览器

参考

欢迎关注我的其它发布渠道