当前位置: 首页 > news >正文

fastap之使用 contextvars 实现上下文变量

文章目录

  • 基本概念
    • 上下文变量
    • `ContextVar` 类
      • 创建上下文变量
      • 设置和获取值
        • 设置值
        • 获取值
        • 临时修改上下文变量
    • `Context`类
      • 创建和管理上下文
    • `contextvars` 在异步编程中的作用
    • `contextvars` 的常见使用场景
  • 例子
    • 解释
    • 测试
      • 浏览器
      • postman
  • 总结

基本概念

contextvars 是 Python 3.7 引入的一个模块,专门用于管理上下文变量(context variables)——这些变量在不同的上下文(context)中能够保持独立且安全的状态,特别是在异步编程中,确保变量在不同的任务或线程间不会相互干扰。它的出现解决了在异步环境中变量共享和传递的难题,是构建线程安全和协程安全应用的关键。

上下文变量

上下文变量是与执行上下文相关联的变量,在异步编程或多线程编程中,每个任务或线程都有自己独立的上下文。使用上下文变量时,不同上下文中的变量值彼此隔离,互不影响。

  • 执行上下文: 执行上下文是程序运行的一个环境,包含当前的变量和其值。不同的协程或线程可以拥有不同的执行上下文。
  • 隔离性: contextvars 提供的变量在每个上下文中是独立的,因此即使在不同的协程或线程中使用相同的上下文变量,它们的值也是彼此隔离的。

ContextVar

contextvars.ContextVar 是管理上下文变量的核心类。使用 ContextVar 创建的变量在不同的上下文中可以拥有不同的值。

创建上下文变量

from contextvars import ContextVar
var = ContextVar('var_name', default='default_value')
  • var_name 是上下文变量的名字,便于调试时使用。
  • default 是可选参数,设置了当上下文中没有该变量时的默认值。

设置和获取值

设置值
var.set('new_value')
  • set()方法将上下文变量的值设置为 'new_value'。在当前上下文中,该变量的值将更新为新值。
获取值
value = var.get()
  • get() 方法返回当前上下文中上下文变量的值。如果当前上下文没有设置值,则返回默认值。
临时修改上下文变量

ContextVar提供了一个 Token 对象,允许临时修改上下文变量的值,并在需要时恢复到之前的状态:

token = var.set('temporary_value')
# 恢复之前的值
var.reset(token)

Context

Context 类是一个更高级的概念,用于显式管理和切换不同的上下文。

创建和管理上下文

from contextvars import Contextctx = Context()
ctx.run(some_function)
  • Context.run() 方法在指定的上下文中执行一个函数 some_function

contextvars 在异步编程中的作用

在异步编程(如 asyncio)中,多个协程可能会并发执行,而 contextvars 确保每个协程中的上下文变量是隔离的。这对于维护请求上下文(如请求 ID、用户会话等)非常重要。

例如,在处理多个 HTTP 请求时,使用contextvars 可以确保每个请求的上下文变量(如请求 ID)不会在不同的请求之间混淆。这样可以方便地在日志中记录每个请求的相关信息,确保数据的一致性和安全性。

contextvars 的常见使用场景

  • 日志记录: 为每个请求生成一个唯一的请求 ID,并在日志中记录该 ID,便于跟踪请求。
  • 请求跟踪: 在异步 Web 框架(如 FastAPI)中,使用 contextvars 可以传递和存储与请求相关的信息,如用户信息、请求头等。
  • 线程安全的全局状态管理: 在多线程或多协程的环境中,可以使用 contextvars 管理每个线程或协程的状态,避免状态冲突。

例子

import contextvars
import os
import uuid
from fastapi import FastAPI, Request
import uvicornapp = FastAPI()# 创建一个 ContextVar 对象,用于存储 request_id
request_id = contextvars.ContextVar("request_id")@app.middleware("http")
async def add_request_id(request: Request, call_next):"""HTTP 中间件,用于在每个请求处理之前生成一个唯一的 request_id,并将其存储在 contextvars 中。Args:request (Request): FastAPI 请求对象,包含客户端的请求信息。call_next (Callable): 用于调用下一个请求处理程序的回调函数,接受 Request 对象并返回 Response 对象。Returns:Response: 经过处理后的 HTTP 响应对象。"""# 为当前请求生成一个唯一的 UUID 作为 request_idunique_request_id = str(uuid.uuid4())request_id.set(unique_request_id)# 继续处理请求,并返回响应response = await call_next(request)# 可以在这里加入 request_id 到响应的 headers 中(可选)response.headers["X-Request-ID"] = unique_request_idreturn response@app.get("/items/")
def read_items():"""处理 GET 请求并返回包含当前请求的 request_id 的字典。Returns:dict: 包含 request_id 的字典,用于跟踪和日志记录。"""# 从 contextvars 中获取当前请求的 request_idcurrent_request_id = request_id.get()return {"request_id": current_request_id}if __name__ == "__main__":uvicorn.run(f"{os.path.basename(__file__).split('.')[0]}:app",host="127.0.0.1",port=8000,reload=True,)

解释

  1. request_id = contextvars.ContextVar("request_id"):
  • 创建一个名为 request_idContextVar 对象,用于在每个请求的生命周期中存储和访问唯一的请求 ID。
  1. add_request_id 中间件:
  • 生成请求 ID: 在处理每个请求之前生成一个唯一的 request_id,并将其存储在上下文变量中。
  • 传递给后续处理程序: 使用 call_next(request) 继续处理请求,并确保 request_id 在整个请求处理中都是可用的。
  • 可选功能: 还可以选择将 request_id 添加到响应的 headers 中,以便客户端可以直接看到该请求的 ID。
  1. read_items 路由处理函数:
  • 获取请求 ID: 从上下文变量中获取当前请求的 request_id 并返回给客户端。

测试

浏览器

在这里插入图片描述

postman

在这里插入图片描述
在这里插入图片描述

总结

contextvars 是 Python 3.7 引入的用于管理上下文变量的模块,旨在解决在异步编程和多线程环境中数据共享和传递的复杂性。上下文变量(Context Variables)允许在不同的执行上下文中存储和访问独立的变量值,确保在并发操作中数据的隔离性和安全性。通过使用 ContextVar 类,可以创建线程安全和协程安全的变量,这些变量的值在各自的上下文中互不干扰,从而避免了全局变量在并发环境下可能引发的冲突和数据污染问题。

在异步编程场景中,contextvars 发挥着重要作用。例如,在处理多个并发请求时,可以为每个请求生成并存储独立的请求 ID,用于日志记录和请求跟踪。这种机制确保了每个协程或线程都能维护自己的状态信息,提升了程序的可靠性和可维护性。除了请求跟踪,contextvars 还广泛应用于管理用户会话、传递请求上下文信息以及维护线程本地数据等场景。

总的来说,contextvars 模块为 Python 提供了一种高效且优雅的方式来处理并发环境下的上下文管理问题。它简化了异步和多线程编程中的状态管理,使开发者能够更专注于业务逻辑的实现,而无需过多担心数据一致性和线程安全等复杂性问题。


http://www.mrgr.cn/news/7946.html

相关文章:

  • 前端面试宝典【设计模式】【4】
  • Netty代码阅读
  • 依赖包更新了但是没有release,如何安装更新的依赖包
  • 链表OJ题——相交链表
  • 电脑ip地址为什么会自己变更?电脑ip怎么改
  • python socket 发生UDP 和 UDPServer接受UDP实例
  • 二叉树的介绍
  • Kali Linux 秘籍 中文版
  • 安全面试常见问题任意文件下载
  • IP进程间的通信方式以及不同主机间的通信方式
  • MySQL 学习笔记之约束与外键
  • 编程思维模式比编程语言内容等更重要也更难传授-2024-机器人篇
  • SpringBoot接口内部从sftp服务器获取文件流实现文件下载
  • 什么是持续集成(持续交付、部署)
  • 组合数dfs
  • Ruby宝石光芒:探索SEO优化的瑰宝工具与库
  • 【Python】家庭用电数据分析Prophet预测
  • 【C++ Primer Plus习题】4.10
  • (贪心) LeetCode 45. 跳跃游戏 II
  • PV、UV、IP:网站流量分析的关键指标