当前位置: 首页 > 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 提供了一种高效且优雅的方式来处理并发环境下的上下文管理问题。它简化了异步和多线程编程中的状态管理,使开发者能够更专注于业务逻辑的实现,而无需过多担心数据一致性和线程安全等复杂性问题。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Ps:首选项 - 常规
  • Unity+Addressable
  • 15.CentOS7升级内核
  • Android 关于设备定屏/黑屏/冻屏/ANR那些事
  • 【北京仁爱堂】脖子歪斜,拉扯疼痛怎么办?规律的生活让痉挛性斜颈的恢复事半功倍!
  • 微信小程序登陆
  • 【精选】基于springboot个人理财APP(源码+设计+辅导)
  • MATLAB 低版本Matlab-读取LAS格式点云文件并可视化(78)
  • C++ 设计模式——迭代器模式
  • 令牌和签名详细介绍+开发使用教程
  • 聚星文社下载地址
  • 光性能 -- OSNR,BER与Q值
  • Nginx 405 not allowed
  • WPS宏实现Sheet页合并功能
  • 区间预测|基于灰狼优化最小二乘支持向量机的多变量回归区间预测Matlab程序GWO-LSSVM-ABKDE
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • Angular4 模板式表单用法以及验证
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • DOM的那些事
  • Elasticsearch 参考指南(升级前重新索引)
  • Git初体验
  • Idea+maven+scala构建包并在spark on yarn 运行
  • Java方法详解
  • markdown编辑器简评
  • Travix是如何部署应用程序到Kubernetes上的
  • web标准化(下)
  • 关于Java中分层中遇到的一些问题
  • 官方解决所有 npm 全局安装权限问题
  • 区块链将重新定义世界
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 如何选择开源的机器学习框架?
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 通过npm或yarn自动生成vue组件
  • 无服务器化是企业 IT 架构的未来吗?
  • 【干货分享】dos命令大全
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • #if和#ifdef区别
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (Java入门)抽象类,接口,内部类
  • (动态规划)5. 最长回文子串 java解决
  • (二)windows配置JDK环境
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (十六)视图变换 正交投影 透视投影
  • (源码分析)springsecurity认证授权
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .net 程序发生了一个不可捕获的异常
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态