最近的工作是基于 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. (cLion、RubyMine、PyCharm、WebStorm、PhpStorm、Appcode、Clion、Idea) 万能破解,获取自己的注冊码

    听说cLion的ide编写c/c++很的棒.今天下载了一个仅仅有30天的使用时间.作为程序猿破解它. 下载破解文件 | 点击下载 |password: 7biu 解压压缩包,然后打开命令行 cd 到解 ...

  2. Ext4.1 chart的使用

    var reportsPanel = Ext.create('Ext.panel.Panel', { id:'reportsPanel',    layout: 'fit',    tbar: [{ ...

  3. 5. MongoDB基本操作语句

    转自:http://blog.51cto.com/shanqiangwu/1653577 #MongoDB中有三元素:数据库,集合,文档,其中“集合”就是对应关系数据库中的“表”,“文档”对应“行”. ...

  4. Mysql-in查询问题

    Mysql-in查询问题 标签(空格分隔): mysql 问题:mysql用in语法查询出来的数据少了好多! 我的实际情况: 数据表: content字段记录着一些选项的id,多个选项用逗号隔开,比如 ...

  5. 关于Fragment的setUserVisibleHint() 方法和onCreateView()的执行顺序

    1:setUserVisibleHint(boolean isVisibleToUser)的方法就很重要,根据方法名来看当前页面是否可见, 里面的boolean值就是判断当前页面是否可见的变量,所以大 ...

  6. pandas学习系列(一):时间序列

    最近参加了天池的一个机场航空人流量预测大赛,需要用时间序列来预测,因此开始使用python的pandas库 发现pandas库功能的确很强大,因此在这记录我的pandas学习之路. # -*- cod ...

  7. inception - resnet

    只有reduction-A是共用的,只是改了其中的几个参数 linear是线性激活. 结构是一样的

  8. Windows7 安装ubuntu双系统

    家里的老笔记本是MBR分区,不支持EFI , 一开始是用U盘安装的,还对着ubuntu官网的教程,下载了官方推荐的那个u盘引导工具,安装依然会报错, 网上查询也有很多种说法,也有说是bug的,无论如何 ...

  9. NodeJS学习笔记 (30)定时器-timers

    https://github.com/chyingp/nodejs-learning-guide

  10. 说说Kindle那些事

    已经不记得是什么时候在哪里听过kindle这玩意的了,反正最开始买kindle还是大四上学期,貌似是2012-9-30,那时候是整个大学最闲的时候,不知道哪天闲的蛋疼一冲动就买了个kindle4黑色款 ...