欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

flask 中的 werkzeug Local,LocalStack 和 LocalProxy 技术应用

程序员文章站 2023-12-29 13:13:52
什么是 Local 为什么使用 Local 为什么使用自定义 Local,而不是 threading.local。这是由内核决定的 1. web 应用在启动之后,是一单线+协成程启动的话,会污染全局变量,无法区分, 2. 使用多线程+协成无法保证,派发请求的工作协程,无法保证同时工作时且分别位于多个 ......

什么是 local

  1. wsgi 每次请求,会把过程进行抽离无状态话,过程数据存储在本次请求的全局变量中,使用到了local. local 作为每次请求的全局命令空间,属于每次请求的私有
  2. localstack 与 local 相似,在 local 基础之上使用堆栈方式进行操作,管理
  3. localproxy 代理类,代理 local 或 localstack 实例

为什么使用 local

  为什么使用自定义 local,而不是 threading.local。这是由内核决定的

    1. web 应用在启动之后,是一单线+协成程启动的话,会污染全局变量,无法区分,

    2. 使用多线程+协成无法保证,派发请求的工作协程,无法保证同时工作时且分别位于多个线程内,彼此互不影响

所以: werkzeug 给出了自己的解决方案:local 和 localstack

为什么使用 localproxy

  那么问题来了:请求的上下文的私有变量存储在 local 和 localstack 中,那在多任务时,每次调用 from flask import request, g, session , 如何保证获取正确的上下文,而不发生混乱?

在  中

def _lookup_req_object(name):  
    top = _request_ctx_stack.top  
    if top is none:  
        raise runtimeerror('working outside of request context')  
    return getattr(top, name)  

_request_ctx_stack = localstack()  
request = localproxy(partial(_lookup_req_object, 'request'))  
session = localproxy(partial(_lookup_req_object, 'session'))

 

在  中, localproxy是一个 local or localstack 的一个代理

@implements_bool
class localproxy(object):
 """"""
  __slots__ = ("__local", "__dict__", "__name__", "__wrapped__")

  def __init__(self, local, name=none):
     object.__setattr__(self, "_localproxy__local", local)
      object.__setattr__(self, "__name__", name)
      if callable(local) and not hasattr(local, "__release_local__"):
        # "local" is a callable that is not an instance of local or
         # localmanager: mark it as a wrapped function.
         object.__setattr__(self, "__wrapped__", local)

   def _get_current_object(self):
      """return the current object.  this is useful if you want the real
      object behind the proxy at a time for performance reasons or because
      you want to pass the object into a different context.
      """
      if not hasattr(self.__local, "__release_local__"):
          return self.__local()
      try:
          return getattr(self.__local, self.__name__)
      except attributeerror:
          raise runtimeerror("no object bound to %s" % self.__name__)

  def __getattr__(self, name):
      if name == "__members__":
          return dir(self._get_current_object())
      return getattr(self._get_current_object(), name)

调用 reqeust:动态 request <= 动态的 _request_ctx_stack.top <= localstack() 每次调用产生使用新的实例与方法结合(request)<= loaclstack.call?

是的,每次调用 request,就会新产生一个proxy实例,每次pop, push, top 均是针对 local 的操作,而 local 的属性赋值与获取均是针对 get_ident 获取的!

如:werkzeug.local.local.py

class local(object):
    __slots__ = ("__storage__", "__ident_func__")

    def __init__(self):
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)
    """"""
    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except keyerror:
            raise attributeerror(name)

    def __setattr__(self, name, value):
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except keyerror:
            storage[ident] = {name: value}

perfect!每次新请求来临时,flask 会把上下文存储在 werkzeug local 中,使用时根据线程或者协程id获取

这样使用有什么好处

  1. 支持底层协程操作,提高扩展并发效率
  2. 避免整个应用对请求上下文的管理与传递
  3. 扩展兼容性perfect,实现了对第三方应用的插拔式扩展
  4. 可阅读性强,操作使用简单,易上手

上一篇:

下一篇: