如何在单台服务器上存储多个Azure用户信息且不阻塞FastAPI服务
FastAPI多用户Azure CSP登录并发问题解决方案
根因定位
- 主进程阻塞:当前调用
az login使用了同步子进程方法(如subprocess.run),会一直等待命令执行完成,卡住FastAPI的工作线程或事件循环 - 登录信息覆盖:Azure CLI默认将所有登录凭证存储在当前系统用户的全局路径
~/.azure下,多用户同时登录时会写入同一份配置文件,后发起的登录请求会直接覆盖之前的凭证信息
落地解决方案
1. 多用户凭证隔离(解决信息覆盖问题)
通过指定独立的Azure CLI配置目录实现用户凭证完全隔离:
- 为每个登录请求生成唯一用户标识(如UUID),在服务本地创建对应专属配置目录,比如
./.azure_creds/<用户UUID> - 执行
az login --use-device-code命令时,注入环境变量AZURE_CONFIG_DIR=<用户专属目录路径>,当前登录的所有凭证只会写入该私有目录,完全不会和其他用户的配置冲突 - 后续调用
az命令获取用户订阅、租户信息时,同样指定对应用户的AZURE_CONFIG_DIR环境变量,即可读取到对应登录凭证
2. 异步执行AZ命令(解决主进程阻塞问题)
结合FastAPI的异步特性,用非阻塞方式执行Azure CLI命令:
- 弃用同步的
subprocess调用方法,改用asyncio.create_subprocess_exec异步执行az系列命令,全程不阻塞FastAPI的事件循环 - 如果你的路由是同步实现的,可以将
az命令执行逻辑提交到ThreadPoolExecutor线程池运行,避免占用FastAPI的工作进程
示例代码参考
import asyncio import os import uuid from fastapi import FastAPI app = FastAPI() # 统一存储所有用户的私有Azure配置目录 USER_CRED_ROOT = "./user_azure_credentials" os.makedirs(USER_CRED_ROOT, exist_ok=True) @app.post("/api/csp/login") async def create_csp_login_session(): # 生成当前请求的用户唯一标识 user_session_id = str(uuid.uuid4()) user_config_path = os.path.join(USER_CRED_ROOT, user_session_id) os.makedirs(user_config_path, exist_ok=True) # 异步执行az login命令,指定私有配置目录 proc = await asyncio.create_subprocess_exec( "az", "login", "--use-device-code", env={**os.environ, "AZURE_CONFIG_DIR": user_config_path}, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, text=True ) stdout, stderr = await proc.communicate() # 解析stdout提取设备登录链接、验证码返回给前端 # 后续请求携带user_session_id即可操作对应用户的登录凭证 return { "session_id": user_session_id, "login_hint": stdout }
额外优化建议
- 可以为用户专属配置目录设置过期自动清理规则,避免长期运行后磁盘空间被无用凭证占用
- 登录完成后可将用户的订阅、租户信息缓存到数据库或Redis中,后续业务请求直接从缓存读取,减少重复调用AZ命令的开销
内容的提问来源于stack exchange,提问作者JongPil
相关产品推荐
相关产品推荐

