最近的工作是基于 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()

执行会发现,很快就会出现同样的错误.

关于魔术方法,详见:

记一次 Apache HUE 优化之因使用 Python 魔术方法而遇到的坑的更多相关文章

  1. Apache性能优化、超时设置,linux 重启apache

    在httpd.conf中去掉Include conf/extra/httpd-default.conf前的#以使httpd-default.php生效.其中调节以下参数Timeout 15 (连接超时 ...

  2. CentOS系统Apache服务器优化详解

    1.Apache优化 Apache能够在CentOS系统正常运行.但是,对于访问量稍大的站点,Apache的这些默认配置是无法满足需求的,我们仍需调整Apache的一些参数,使Apache能够在大访问 ...

  3. Apache性能优化总结

    1.介绍 首先要了解Apache采用的MPM(Multi -Processing Modules,多道处理模块),MPM是Apache的核心,它的作用是管理网络连接.调度请求.Apache2.0中MP ...

  4. 【BZOJ4654】【NOI2016】国王饮水记(动态规划,斜率优化)

    [BZOJ4654][NOI2016]国王饮水记(动态规划,斜率优化) 题面 BZOJ 洛谷 题解 首先肯定是找性质. 明确一点,比\(h_1\)小的没有任何意义. 所以我们按照\(h\)排序,那么\ ...

  5. linux apache服务器优化建议整理(很实用)

    转载:http://www.cnblogs.com/zhongbin/archive/2013/06/11/3131865.html 1.apache服务器的time_wait过多 fin_wait1 ...

  6. apache配置优化 - 解决apache环境下网站访问速度慢的问题(重点参考)

    如果apche访问量过大,将会导致页面打开迟缓,下载速度也降低,如果由于经费和环境问题,集群方案没有得以应用.可以通过对Apache2增加模块MPM来进行优化, 这里我选择线程型MPM加以优化:  开 ...

  7. 25个Apache性能优化技巧推荐

    25个Apache性能优化技巧推荐 Apache至今仍处于web服务器领域的霸主,无人撼动,没有开发者不知道.本篇文章介绍25个Apache性能优化的技巧,如果你能理解并掌握,将让你的Apache性能 ...

  8. Apache网页优化与安全

    目录 一.Apache网页优化 1.1.概述 1.2.gzip介绍 1.3.Apache的压缩模块 二.网页压缩实验 2.1.检查是否安装mod_deflate模块 2.2.重新编译安装Apache添 ...

  9. Apache网页优化

    目录: 一.Apache网页优化概述 二.网页压缩 三.网页缓存 四.隐藏版本信息 五.Apache防盗链 一.Apache网页优化概述 在企业中,部署Apache后只采用默认的配置参数,会引发网站很 ...

随机推荐

  1. Qt之图形(渐变填充)

    简述 QGradient可以和QBrush组合使用,来指定渐变填充. Qt目前支持三种类型的渐变填充: QLinearGradient:显示从起点到终点的渐变. QRadialGradient:以圆心 ...

  2. 蓝的成长记——追逐DBA(18):小机上WAS集群故障,由一次更换IP引起

    原创作品.出自 "深蓝的blog" 博客,欢迎转载,转载时请务必注明出处.否则追究版权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong ...

  3. HDU 5344(MZL's xor-(ai+aj)的异或和)

    MZL's xor Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total ...

  4. flatMap作用

    总结:1. map会将每一条输入映射为一个新对象.{苹果,梨子}.map(去皮) = {去皮苹果,去皮梨子} 其中: “去皮”函数的类型为:A => B 2.flatMap包含两个操作:会将每一 ...

  5. ubuntu16.04下snort的安装(官方文档安装)(图文详解)

    不多说,直接上干货! 最近为了科研,需要安装和使用Snort. snort的官网 https://www.snort.org/ Snort作为一款优秀的开源主机入侵检测系统,在windows和Linu ...

  6. C#篇(二)——属性的实质

    属性的内部实现其实就是方法 我们平时写的代码: class Student { private int age; public int Age { get { return age; } set { ...

  7. 集合HashSet的使用

    集合中的HashSet底层是通过Hash表实现,HashSet的特点是元素唯一,但用到Hash表就跟hashCode()有了密不可分的联系,所以HashSet的唯一性是通过hashCode()方法来保 ...

  8. 一篇文章助你理解Python2中字符串编码问题

    前几天给大家介绍了unicode编码和utf-8编码的理论知识,没来得及上车的小伙伴们可以戳这篇文章:浅谈unicode编码和utf-8编码的关系.下面在Python2环境中进行代码演示,分别Wind ...

  9. javascript实现多线程 Concurrent.Thread.js

    在这次我的项目中,因为前端要检测硬件加载并识别,再向后台请求发送数据,然后再返回的相应的配置文件!在这过程,要好时好几秒钟,严重影响体验效果,所以在网上靠看的方案,运用多线程去处理,这效果明显改善! ...

  10. AtCoderBeginner091-C 2D Plane 2N Points 模拟问题

    题目链接:https://abc091.contest.atcoder.jp/tasks/arc092_a 题意 On a two-dimensional plane, there are N red ...