<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python 探針實(shí)現(xiàn)原理

          共 10894字,需瀏覽 22分鐘

           ·

          2020-12-04 17:04

          號(hào)外:
          本號(hào)免費(fèi)提供 CSDN 資源下載,需要的伙伴公眾號(hào)后臺(tái)回復(fù)【CSDN】

          作者:mozillazg     原文鏈接:

          https://segmentfault.com/a/1190000004889212

          關(guān)于 Python 的導(dǎo)入機(jī)制,我以前寫過一篇文章,非常詳細(xì),[深入探討 Python 的 import 機(jī)制:實(shí)現(xiàn)遠(yuǎn)程導(dǎo)入模塊]另外,今天再給你推薦這篇文章,同樣是介紹 Python 的導(dǎo)入機(jī)制,和上面的文章一起食用更佳。本文呢,將簡單講述一下 Python 探針的實(shí)現(xiàn)原理。同時(shí)為了驗(yàn)證這個(gè)原理,我們也會(huì)一起來實(shí)現(xiàn)一個(gè)簡單的統(tǒng)計(jì)指定函數(shù)執(zhí)行時(shí)間的探針程序。探針的實(shí)現(xiàn)主要涉及以下幾個(gè)知識(shí)點(diǎn):

          • sys.meta_path
          • sitecustomize.py

          sys.meta_path

          sys.meta_path 這個(gè)簡單的來說就是可以實(shí)現(xiàn) import hook 的功能, 當(dāng)執(zhí)行 import 相關(guān)的操作時(shí),會(huì)觸發(fā) sys.meta_path 列表中定義的對(duì)象。關(guān)于 sys.meta_path 更詳細(xì)的資料請(qǐng)查閱 python 文檔中 sys.meta_path 相關(guān)內(nèi)容以及 PEP 0302 。
          sys.meta_path 中的對(duì)象需要實(shí)現(xiàn)一個(gè) find_module 方法, 這個(gè) find_module 方法返回 None 或一個(gè)實(shí)現(xiàn)了 load_module 方法的對(duì)象 (代碼可以從 github 上下載 part1_) :

          import sysclass MetaPathFinder:    def find_module(self, fullname, path=None):        print('find_module {}'.format(fullname))        return MetaPathLoader()class MetaPathLoader:    def load_module(self, fullname):        print('load_module {}'.format(fullname))        sys.modules[fullname] = sys        return syssys.meta_path.insert(0, MetaPathFinder())if __name__ == '__main__':    import http    print(http)    print(http.version_info)

          load_module 方法返回一個(gè) module 對(duì)象,這個(gè)對(duì)象就是 import 的 module 對(duì)象了。比如我上面那樣就把 http 替換為 sys 這個(gè) module 了。

          $ python meta_path1.pyfind_module httpload_module http
               
                'sys' (built-in)>sys.version_info(major=3, minor=5, micro=1, releaselevel=
                'final', serial=0)
                

          通過 sys.meta_path 我們就可以實(shí)現(xiàn) import hook 的功能:當(dāng) import 預(yù)定的 module 時(shí),對(duì)這個(gè) module 里的對(duì)象來個(gè)貍貓換太子, 從而實(shí)現(xiàn)獲取函數(shù)或方法的執(zhí)行時(shí)間等探測(cè)信息。上面說到了貍貓換太子,那么怎么對(duì)一個(gè)對(duì)象進(jìn)行貍貓換太子的操作呢?對(duì)于函數(shù)對(duì)象,我們可以使用裝飾器的方式來替換函數(shù)對(duì)象(代碼可以從 github 上下載 part2) :

          import functoolsimport timedef func_wrapper(func):    @functools.wraps(func)    def wrapper(*args, **kwargs):        print('start func')        start = time.time()        result = func(*args, **kwargs)        end = time.time()        print('spent {}s'.format(end - start))        return result    return wrapperdef sleep(n):    time.sleep(n)    return nif __name__ == '__main__':    func = func_wrapper(sleep)    print(func(3))

          執(zhí)行結(jié)果:

          $ python func_wrapper.pystart funcspent 3.004966974258423s3

          下面我們來實(shí)現(xiàn)一個(gè)計(jì)算指定模塊的指定函數(shù)的執(zhí)行時(shí)間的功能(代碼可以從 github 上下載 part3) 。假設(shè)我們的模塊文件是 hello.py:

          import timedef sleep(n):    time.sleep(n)    return n

          我們的 import hook 是 hook.py:

          import functoolsimport importlibimport sysimport time_hook_modules = {'hello'}class MetaPathFinder:    def find_module(self, fullname, path=None):        print('find_module {}'.format(fullname))        if fullname in _hook_modules:            return MetaPathLoader()class MetaPathLoader:    def load_module(self, fullname):        print('load_module {}'.format(fullname))        # ``sys.modules`` 中保存的是已經(jīng)導(dǎo)入過的 module        if fullname in sys.modules:            return sys.modules[fullname]        # 先從 sys.meta_path 中刪除自定義的 finder        # 防止下面執(zhí)行 import_module 的時(shí)候再次觸發(fā)此 finder        # 從而出現(xiàn)遞歸調(diào)用的問題        finder = sys.meta_path.pop(0)        # 導(dǎo)入 module        module = importlib.import_module(fullname)        module_hook(fullname, module)        sys.meta_path.insert(0, finder)        return modulesys.meta_path.insert(0, MetaPathFinder())def module_hook(fullname, module):    if fullname == 'hello':        module.sleep = func_wrapper(module.sleep)def func_wrapper(func):    @functools.wraps(func)    def wrapper(*args, **kwargs):        print('start func')        start = time.time()        result = func(*args, **kwargs)        end = time.time()        print('spent {}s'.format(end - start))        return result    return wrapper

          測(cè)試代碼:

          >>> import hook>>> import hellofind_module helloload_module hello>>>>>> hello.sleep(3)start funcspent 3.0029919147491455s3>>>

          其實(shí)上面的代碼已經(jīng)實(shí)現(xiàn)了探針的基本功能。不過有一個(gè)問題就是上面的代碼需要顯示的 執(zhí)行 import hook 操作才會(huì)注冊(cè)上我們定義的 hook。那么有沒有辦法在啟動(dòng) python 解釋器的時(shí)候自動(dòng)執(zhí)行 import hook 的操作呢?答案就是可以通過定義 sitecustomize.py 的方式來實(shí)現(xiàn)這個(gè)功能。

          sitecustomize.py

          簡單的說就是,python 解釋器初始化的時(shí)候會(huì)自動(dòng) import PYTHONPATH 下存在的 sitecustomizeusercustomize 模塊:實(shí)驗(yàn)項(xiàng)目的目錄結(jié)構(gòu)如下(代碼可以從 github 上下載 part4) :

          $ tree.├── sitecustomize.py└── usercustomize.py

          sitecustomize.py:

          $ cat sitecustomize.pyprint('this is sitecustomize')

          usercustomize.py:

          $ cat usercustomize.pyprint('this is usercustomize')

          把當(dāng)前目錄加到 PYTHONPATH 中,然后看看效果:

          export PYTHONPATH=.$ pythonthis is sitecustomize       <----this is usercustomize       <----Python 3.5.1 (default, Dec 24 2015, 17:20:27)[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwinType "help""copyright""credits" or "license" for more information.>>>

          可以看到確實(shí)自動(dòng)導(dǎo)入了。所以我們可以把之前的探測(cè)程序改為支持自動(dòng)執(zhí)行 import hook (代碼可以從 github 上下載 part5) 。目錄結(jié)構(gòu):

          $ tree.├── hello.py├── hook.py├── sitecustomize.py

          sitecustomize.py:

          $ cat sitecustomize.pyimport hook

          結(jié)果:

          export PYTHONPATH=.$ pythonfind_module usercustomizePython 3.5.1 (default, Dec 24 2015, 17:20:27)[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwinType "help""copyright""credits" or "license" for more information.find_module readlinefind_module atexitfind_module rlcompleter>>>>>> import hellofind_module helloload_module hello>>>>>> hello.sleep(3)start funcspent 3.005002021789551s3

          不過上面的探測(cè)程序其實(shí)還有一個(gè)問題,那就是需要手動(dòng)修改 PYTHONPATH 。用過探針程序的朋友應(yīng)該會(huì)記得, 使用 newrelic 之類的探針只需要執(zhí)行一條命令就 可以了:newrelic-admin run-program python hello.py 實(shí)際上修改 PYTHONPATH 的操作是在newrelic-admin 這個(gè)程序里完成的。下面我們也要來實(shí)現(xiàn)一個(gè)類似的命令行程序,就叫 agent.py 吧。

          agent

          還是在上一個(gè)程序的基礎(chǔ)上修改。先調(diào)整一個(gè)目錄結(jié)構(gòu),把 hook 操作放到一個(gè)單獨(dú)的目錄下, 方便設(shè)置 PYTHONPATH 后不會(huì)有其他的干擾(代碼可以從 github 上下載 part6 )。

          $ mkdir bootstrap$ mv hook.py bootstrap/_hook.py$ touch bootstrap/__init__.py$ touch agent.py$ tree.├── bootstrap│   ├── __init__.py│   ├── _hook.py│   └── sitecustomize.py├── hello.py├── test.py├── agent.py

          bootstrap/sitecustomize.py 的內(nèi)容修改為:

          $ cat bootstrap/sitecustomize.pyimport _hook

          agent.py 的內(nèi)容如下:

          import osimport syscurrent_dir = os.path.dirname(os.path.realpath(__file__))boot_dir = os.path.join(current_dir, 'bootstrap')def main():    args = sys.argv[1:]    os.environ['PYTHONPATH'] = boot_dir    # 執(zhí)行后面的 python 程序命令    # sys.executable 是 python 解釋器程序的絕對(duì)路徑 ``which python``    # >>> sys.executable    # '/usr/local/var/pyenv/versions/3.5.1/bin/python3.5'    os.execl(sys.executable, sys.executable, *args)if __name__ == '__main__':    main()

          test.py 的內(nèi)容為:

          $ cat test.pyimport sysimport helloprint(sys.argv)print(hello.sleep(3))

          使用方法:

          $ python agent.py test.py arg1 arg2find_module usercustomizefind_module helloload_module hello['test.py''arg1''arg2']start funcspent 3.005035161972046s3

          至此,我們就實(shí)現(xiàn)了一個(gè)簡單的 python 探針程序。當(dāng)然,跟實(shí)際使用的探針程序相比肯定是有 很大的差距的,這篇文章主要是講解一下探針背后的實(shí)現(xiàn)原理。文中的代碼:https://github.com/mozillazg/apm-python-agent-principle

              
          -END-

               
          推薦閱讀
          你們猜猜,哪個(gè)省的女生最愛豐胸??
          Python 下載的 11 種姿勢(shì),一種比一種高級(jí)!

                
          關(guān)注「Python 知識(shí)大全」,做全棧開發(fā)工程師
          歲月有你 惜惜相處

          回復(fù) 【資料】獲取高質(zhì)量學(xué)習(xí)資料


          得本文對(duì)你有幫助?請(qǐng)分享給更多人
          點(diǎn)「在看」的人都變好看了哦
          瀏覽 33
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲天堂福利视频 | 日韩激情一级片 | 精品国产一区二区三区久久久狼,91精品一 | 欧美成人精品一区二区特级毛片 | 日韩AV一卡二卡 |