使用 FastAPI 计算全局变量的请求数

Count number of requests with global variable using FastAPI

我想计算特定 URL 路径中的请求数。

app = FastAPI()
counter = 0

@app.get("/do_something")
async def do_something():
    global counter
    counter += 1
    return {"message": "Hello World"}

这段代码可以吗? 计数器应该是线程安全的?安全吗? 这是计算请求的正确方法(没有数据库)吗? 在这种情况下,“do_something”函数中的“async”是否有意义? 以及如何让它与多个工人一起工作?

根据This issue,非异步端点将在线程池中处理。

Non-async def endpoints (i.e., plain def endpoints) get executed in a threadpool, so it is possible to run into thread safety issues if you make modifications to shared global objects or similar.

If it is that it uses a thread lock, then yeah, that's fine. It will prevent any other thread from touching that thing until the first one finishes, so that's fine.

所以代码可以是这样的:

import threading

app = FastAPI()
counter = 0
lock = threading.Lock()

@app.get("/do_something")
def do_something():
    global counter

    with lock:
        counter += 1
        # some other thread-safe code here

    return {"message": "Hello World"}

此代码不安全,因为您没有使用锁。我想你认为 += 操作是原子的,所以没有锁使用是安全的,但事实并非如此。为了保护您的状态,您需要锁。 asyncio 库提供锁 https://docs.python.org/3/library/asyncio-sync.html.

import asyncio

app = FastAPI()
counter = 0
lock = asyncio.Lock()

@app.get("/do_something")
async def do_something():
    global counter

    async with lock:
        counter += 1
        # some other thread-safe code here

    return {"message": "Hello World"}

如果您使用 1 个 worker,您应该使用 asyncio 锁,因为 fastAPI 是异步的。

import asyncio

app = FastAPI()
counter_lock = asyncio.Lock()
counter = 0

@app.get("/do_something")
async def do_something():
    global counter

    async with counter_lock:
        counter += 1

    return {"message": "Hello World"}

但是如果有超过 1 个 worker 将无法工作,因为他们不共享相同的内存。应该使用缓存机制或数据库,如解释的那样