記一次 Apache HUE 優化之因使用 Python 魔術方法而遇到的坑

来源:http://www.cnblogs.com/scharfsinnig/archive/2017/08/21/7405976.html
-Advertisement-
Play Games

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

執行會發現,很快就會出現同樣的錯誤.
關於魔術方法,詳見:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 轉載請說明來源: http://www.cnblogs.com/lizhilin2016/p/7390079.html 最近lz 在開始做一個新的Demo, 在項目中集成了bugly用於收集項目中的崩潰日誌, 集成Bmob用於作為後臺資料庫, 集成了友盟用於集成第三方分享, 當然了, 這些都有很多方 ...
  • 代碼: AppDelegate.m ...
  • 1 概述 1 概述 本篇文章簡要對事物與鎖的分析比較詳細,因此就轉載了。 2 具體內容 2 具體內容 併發可以定義為多個進程同時訪問或修改共用數據的能力。處於活動狀態而互不幹涉的併發用戶進程的數量越多,資料庫系統的併發性就越好。當一個正在修改數據的進程阻止了其他進程讀取該數據,或者當一個正在讀取數據 ...
  • 1 概述 1 概述 本文將結合MSDN簡要概述JSON數據。 2 具體內容 2 具體內容 JSON 是一種流行的數據格式,用於在現代 Web 和移動應用程式中交換數據。 JSON 還可用於在 Microsoft Azure DocumentDB 等 NoSQL 資料庫中存儲非結構化數據。 許多 RE ...
  • 本文根據《MySQL必知必會》(Ben Forta著,2009)整理,基於MySQL4.1-5,可作為深入研究MySQL之前的漱口篇。(基本語句、正則表達式、聯結、全文本搜索、增刪改查、存儲過程、游標、觸發器、事務、索引)。 ...
  • 1 蠕蟲複製 蠕蟲複製:從已有的數據中去獲取數據,然後將數據又進行新增操作,數據成倍增加。 表創建高級操作:從已有創建新表(複製表結構) 蠕蟲複製:先查出數據,然後將查出的數據新增一遍。 蠕蟲複製的意義 從已有表拷貝數據到新表中 可以迅速的讓表中的數據膨脹到一定的數量級:測試表的壓力以及效率。 從已 ...
  • 圖資料庫初試之Neo4j 自從進入了移動互聯網時代,各種新事物出現的速度都好像坐上了宇宙飛船,幾乎隔幾天一個新概念。就拿資料庫而言,什麼Oracle、DB2、SQL Server、MySQL,這些你都得知道,然後是以MongoDB、HBase等為代表的NoSQL資料庫,這幾年圖資料庫也很快流行起來, ...
  • PG視圖分為兩種,一種是物化視圖,一種是一般視圖。本篇文章主要寫一般視圖哪些事兒。所謂一般視圖,通俗點說,就是由查詢語句定義的虛擬表。視圖中的數據可能來自一張或多張表。 1. 視圖創建語句 2. 創建視圖示例 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...