记一次 Apache HUE 优化之因使用 Python 魔术方法而遇到的坑

   2023-02-09 学习力0
核心提示:最近的工作是基于 Apache HUE 做二次开发.刚接手 HUE 的代码的时候,内心是崩溃的:开源的代码,风格很多种, 代码比较杂乱; 虽是基于 Django 开发的,但是项目的结构改变很大; 很多地方留下了坑; 前人基于此项目做了一些开发, 考虑欠佳, 杂乱中又增添了些杂乱....

最近的工作是基于 Apache HUE 做二次开发.刚接手 HUE 的代码的时候,内心是崩溃的:开源的代码,风格很多种, 代码比较杂乱; 虽是基于 Django 开发的,但是项目的结构改变很大; 很多地方留下了坑; 前人基于此项目做了一些开发, 考虑欠佳, 杂乱中又增添了些杂乱......
没办法,既然参与了进来,就贡献自己的一份力量.
今天在优化 Lib Sentry 的时候,不经意间就出现了一个 Bug. 项目中,有处使用了全局锁的形式,来将 Sentry 的链接存入到全局变量中. 我试着用 Django 缓存的形式将其替换,以提高代码的效率.但是, run 起来的时候,很快就出现了调用栈溢出的现象.为什么会出现这种情况? 难道是导入不合理?先就是一顿 import review. 发现并没有类似的循环导入, 目录结构也还OK啊.那问题出现哪呢? 没办法,借助日志, 发现了一些问题:

File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,
File "/home/hp/Project/platform/desktop/core/src/desktop/lib/thrift_util.py" in __getattr__
  364.     superclient = _connection_pool.get_client(self.conf,

日志的信息显示,在 thrift_utils.py 文件中,发现一直有个方法在执行,且是同一行.为什么?看源码.

class PooledClient(object):
  """
  A wrapper for a SuperClient
  """
  def __init__(self, conf):
    self.conf = conf

  def __getattr__(self, attr_name):
    if attr_name in self.__dict__:
      return self.__dict__[attr_name]

    # Fetch the thrift client from the pool
    superclient = _connection_pool.get_client(self.conf,
        get_client_timeout=self.conf.timeout_seconds)

    # Fetch the attribute. If it's callable, wrap it in a wrapper that re-gets
    # the client.
    try:
      attr = getattr(superclient, attr_name)

      if callable(attr):
        return self._wrap_callable(attr_name)
      else:
        return attr
    finally:
      self._return_client(superclient)

这是 HUE 源码的片段, 抛错就是从这里出现的. 发现一直在执行 superclient = _connection_pool.get_client(... 这块.WHY? 难道是 conf 没有?试着去加些打印信息,发现果然是没有 conf. 不能啊!为什么会没有 conf 呢?
于是,再看下Django抛出的 error 信息,发现了一些信息:

py2.7.egg/django/core/cache/backends/locmem.py" in get
  48.                     return pickle.loads(pickled)

程序是执行到这之后,才一直在重复执行上面的错误的.为什么 loads 的时候会出错呢? 首先猜想的是, loads 的时候,因为什么原因导致了 PooledClient 的 object 没有 conf 属性. 那就看下 pickle.loads. 看完之后,再借助了 log 信息, 发现其是因为去寻找 __setstate__ 属性的时候才导致了这种错误.好了,至此,问题就得以描述清楚了.
之所以调用 Django core cache 导致了调用栈溢出, 是因为 Django 在 cache get 的方法中将存储的数据反序列化成对象,而这个对象在此时还没有生成,且序列化的时候要去调用 __setstate__ 方法, 但是类中没有定义,只是定义了 __getattr__ 方法.而 __getattr__ 方法中又使用了 conf 方法, 这时候 conf 还没有, 所以,又触发了 __getattr__ 方法的执行.如此反复,导致了最终的调用栈溢出现象.
好了,既然找到问题了,那就解决吧.
我这里是自己实现了 __getstate__, __setstate__ 的魔术方法,这样,就可以解决了找不到 __setstate__ 的问题. 还有一种解决方法,就是将 conf 定位为 类属性. 这样是从找不到 conf 源头解决问题.
问题解决,开始总结下 Python 魔术方法.
__setstate__, __getstate__ 方法在 pickle 序列化和反序列化的时候会触发执行. getattr 是当 object 的某个属性找不到的时候触发执行.
下面是我模拟的测试代码:

# coding=utf8

import pickle
import StringIO


class PeopleObject(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def display(self):
        print 'name:', self.name, 'address:', self.age

    def __getattr__(self, attr_name):
        if attr_name in self.__dict__:
            return self.__dict__[attr_name]
        else:
            print self.name

    def __getstate__(self):
        state = self.__dict__.copy()
        return state

    # def __setstate__(self, state):
    #     print state
    #     self.__dict__.update(state)


hanmeimei = PeopleObject("Han Meimei", 18)
hanmeimei.display()
store_file = StringIO.StringIO()

pickle.dump(hanmeimei, store_file, 0)  # 序列化

# del Person #反序列的时候,必须能找到对应类的定义。否则反序列化操作失败。
store_file.seek(0)
hanmeimei_ins = pickle.load(store_file)  # 反序列化
hanmeimei_ins.display()
store_file.close()

执行会发现,很快就会出现同样的错误.
关于魔术方法,详见:

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Apache80端口被占用解决办法
    Apache80端口被占用解决办法
    1. win+R,输入 cmd,打开命令行窗口2. 命令行输入netstat -ano3. 找到80端口及对应进程 4. 在任务管理器中的进程处查看与上述80端口对应的PID相同的进程,并关闭。如果没有PID,选择“查看”--"选择列"--勾选“PID”。4. 关闭进程。如果进程为上述系统服务,
    03-08
  • Hadoop中mapreduce运行WordCount程序报错Error:
    这个问题是因为map的方法参数与继承mapper定义的参数类型不一致导致的,应该将Mapper的key参数类型设置成Object,就可以解决这个问题 
    03-08
  • 使用 Apache Hudi 实现 SCD-2(渐变维度)
    使用 Apache Hudi 实现 SCD-2(渐变维度)
    数据是当今分析世界的宝贵资产。 在向最终用户提供数据时,跟踪数据在一段时间内的变化非常重要。 渐变维度 (SCD) 是随时间推移存储和管理当前和历史数据的维度。 在 SCD 的类型中,我们将特别关注类型 2(SCD 2),它保留了值的完整历史。 每条记录都包含有
    03-08
  • 一个用 Python 分析 Apache 日志的故事
    一个用 Python 分析 Apache 日志的故事
    介绍不久前,公司里有人告诉我“我想知道 Apache 的访问日志是否可以用来做一些事情”。数据分析,Apache,发文章,我是初学者,写的不好请见谅。访问日志分析导入模块我正在使用以下模块。# データの処理import pandas as pdimport numpy as np# グラフ表示i
    03-08
  • [转]用apache反向代理解决单外网ip对应内网多个
    用apache反向代理解决单外网ip对应内网多个web主机的问题  转载一个有独立外网IP,需内网服务器对外发布的例子,是应用apache虚拟主机的。来源地址:http://www.itshantou.com/Servers/web/06/10/44219.html    几年前开始在学校的服务器上建网站,那时
    02-10
  • Apache service named reported the following
    apache启动失败报错:The Apache service named reported the following error: AH00451: no listening sockets available, shutting down . The Apache service named reported the following error: (OS 10055)由于系统缓冲区空间不足或队列已满,不能执行
    02-10
  • struts布局管理---SiteMesh一个优于Apache Tile
    1. SiteMesh的基本原理       一个请求到服务器后,如果该请求需要sitemesh装饰,服务器先解释被请求的资源,然后根据配置文件 获得用于该请求的装饰器,最后用装饰器装饰被请求资源,将结果一同返回给客户端浏览器。 2. 如何使用SiteMesh    这里以st
    02-10
  • linux 安装 apache2.2.31
     Linux下安装和配置Apache 概要:本文介绍在CentOS5.4 Linux中安装和配置Apache2.2.14,并且实现Apache和Tomcat6的整合。文章分为三部分,分别是删除系统自带的Apache、安装Apache2.2.14和配置Apache2.2.14。 文章中介绍的知识也可以在其它版本的Linux中
    02-10
  • Apache CXF使用Jetty发布WebService
    Apache CXF使用Jetty发布WebService
    一、概述Apache CXF提供了用于方便地构建和开发WebService的可靠基础架构。它允许创建高性能和可扩展的服务,可以部署在Tomcat和基于Spring的轻量级容器中,也可以部署在更高级的服务器上,例如Jboss、WebSphere或WebLogic。 CXF提供了以下功能:WebService
    02-10
  • apache下ab.exe使用方法。。 apache ab工具
    自己在cmd中写了半天的路径也没有写对。。最后网上的一个哥们告诉我说没有共同语言了。。。毛线啊 差距确实很大!大能猫死panda早晚干掉你,叫你丫整天嘲讽我!比如我的ab.exe在D盘的wamp文件夹下apache文件夹下bin文件夹下。那么在cmd中可以这么写:"D:\wamp
    02-10
点击排行