contextvars使用
flask的隐式传递
flask可以隐式传递变量(request,g),使用的是ThreadLocal(本地线程)对象,但是flask并不是使用python自带的ThreadLocal,而是自己实现了一个Local类,除了支持线程还支持了 Greenlet 的协程
Q: 那为什么不用全局变量呢? A: 由于存在 GIL,全局变量的修改必须加锁,会影响效率
contextvars
contextvars,这个模块提供了一组接口,可用于管理、储存、访问局部 Context 的状态
示例
请求前钩子
# hook.py
from contextvars import ContextVar
# 定义上下文变量
RequestID = ContextVar('request_id')
@web.middleware
async def before_middleware(request: Request, handler):
# 设置唯一key
request_id = RequestID.set(str(uuid4()))
request['request_id'] = request_id
# logger.py
import logging
class LoggerFormatter(logging.Formatter):
def __init__(self, msg=LOG_FORMAT):
logging.Formatter.__init__(self, msg)
def format(self, record):
record_clone = copy(record)
try:
# 全局更改,这样在多handler比如文件输出和console输出会跑两次这个函数
from trpc_unit.aiohttp_middleware import RequestID
unq_id = RequestID.get(None)
record_clone.msg = "[{}] ".format(unq_id) + record_clone.msg
except:
pass
return logging.Formatter.format(self, record_clone)
官方用法
上下文变量¶
class contextvars.ContextVar(name[, *, default])
此类用于声明一个新的上下文变量,如:
var: ContextVar[int] = ContextVar('var', default=42)
name 参数用于内省和调试,必需。
调用 ContextVar.get() 时,如果上下文中没有找到此变量的值,则返回可选的仅命名参数 default 。
重要: 上下文变量应该在顶级模块中创建,且永远不要在闭包中创建。 Context 对象拥有对上下文变量的强引用,这可以让上下文变量被垃圾收集器正确回收。
name
上下文变量的名称,只读属性。
3.7.1 新版功能.
get([default])
返回当前上下文中此上下文变量的值。
如果当前上下文中此变量没有值,则此方法会:
如果提供了得 话,返回传入的 default 值;或者
返回上下文变量本身的默认值, 如果创建此上下文变量时提供了默认值;或者
抛出 LookupError 异常。
set(value)
调用此方法设置上下文变量在当前上下文环境中的值。
必选参数 value 是上下文变量的新值。
返回一个 Token 对象,可通过 ContextVar.reset() 方法将上下文变量还原为之前某个状态。
reset(token)
将上下文变量重置为调用 ContextVar.set() 之前、创建 token 时候的状态。
参考链接
https://gist.github.com/messa/c538fc267550ec67a1fed244183dcf1e