成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

littleGrow / 1692人閱讀

摘要:本文將簡單講述一下探針的實(shí)現(xiàn)原理。探針的實(shí)現(xiàn)主要涉及以下幾個(gè)知識點(diǎn)這個(gè)簡單的來說就是可以實(shí)現(xiàn)的功能,當(dāng)執(zhí)行相關(guān)的操作時(shí),會觸發(fā)列表中定義的對象。當(dāng)然,跟實(shí)際使用的探針程序相比肯定是有很大的差距的,這篇文章主要是講解一下探針背后的實(shí)現(xiàn)原理。

本文將簡單講述一下 Python 探針的實(shí)現(xiàn)原理。 同時(shí)為了驗(yàn)證這個(gè)原理,我們也會一起來實(shí)現(xiàn)一個(gè)簡單的統(tǒng)計(jì)指定函數(shù)執(zhí)行時(shí)間的探針程序。

探針的實(shí)現(xiàn)主要涉及以下幾個(gè)知識點(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í),會觸發(fā) sys.meta_path 列表中定義的對象。 關(guān)于 sys.meta_path 更詳細(xì)的資料請查閱 python 文檔中 sys.meta_path 相關(guān)內(nèi)容以及 PEP 0302 。

sys.meta_path 中的對象需要實(shí)現(xiàn)一個(gè) find_module 方法, 這個(gè) find_module 方法返回 None 或一個(gè)實(shí)現(xiàn)了 load_module 方法的對象 (代碼可以從 github 上下載 part1_) :

import sys


class 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 sys

sys.meta_path.insert(0, MetaPathFinder())

if __name__ == "__main__":
    import http
    print(http)
    print(http.version_info)

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

$ python meta_path1.py
find_module http
load_module http

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í),對這個(gè) module 里的對象來個(gè)貍貓換太子, 從而實(shí)現(xiàn)獲取函數(shù)或方法的執(zhí)行時(shí)間等探測信息。

上面說到了貍貓換太子,那么怎么對一個(gè)對象進(jìn)行貍貓換太子的操作呢? 對于函數(shù)對象,我們可以使用裝飾器的方式來替換函數(shù)對象(代碼可以從 github 上下載 part2) :

 ?
 import functools
 import time}
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


def sleep(n):
    time.sleep(n)
    return n

if __name__ == "__main__":
    func = func_wrapper(sleep)
    print(func(3))

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

$ python func_wrapper.py
start func
spent 3.004966974258423s
3

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

假設(shè)我們的模塊文件是 hello.py:

import time


def sleep(n):
    time.sleep(n)
    return n

我們的 import hook 是 hook.py:

import functools
import importlib
import sys
import 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 module

sys.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

測試代碼:

>>> import hook
>>> import hello
find_module hello
load_module hello
>>>
>>> hello.sleep(3)
start func
spent 3.0029919147491455s
3
>>>

其實(shí)上面的代碼已經(jīng)實(shí)現(xiàn)了探針的基本功能。不過有一個(gè)問題就是上面的代碼需要顯示的 執(zhí)行 import hook 操作才會注冊上我們定義的 hook。

那么有沒有辦法在啟動 python 解釋器的時(shí)候自動執(zhí)行 import hook 的操作呢? 答案就是可以通過定義 sitecustomize.py 的方式來實(shí)現(xiàn)這個(gè)功能。

sitecustomize.py

簡單的說就是,python 解釋器初始化的時(shí)候會自動 import PYTHONPATH 下存在的 sitecustomizeusercustomize 模塊:

實(shí)驗(yàn)項(xiàng)目的目錄結(jié)構(gòu)如下(代碼可以從 github 上下載 part4) :

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

sitecustomize.py:

$ cat sitecustomize.py
print("this is sitecustomize")

usercustomize.py:

$ cat usercustomize.py
print("this is usercustomize")

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

$ export PYTHONPATH=.
$ python
this 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 darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

可以看到確實(shí)自動導(dǎo)入了。所以我們可以把之前的探測程序改為支持自動執(zhí)行 import hook (代碼可以從 github 上下載 part5) 。

目錄結(jié)構(gòu):

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

sitecustomize.py:

$ cat sitecustomize.py
import hook

結(jié)果:

$ export PYTHONPATH=.
$ python
find_module 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 darwin
Type "help", "copyright", "credits" or "license" for more information.
find_module readline
find_module atexit
find_module rlcompleter
>>>
>>> import hello
find_module hello
load_module hello
>>>
>>> hello.sleep(3)
start func
spent 3.005002021789551s
3

不過上面的探測程序其實(shí)還有一個(gè)問題,那就是需要手動修改 PYTHONPATH 。 用過探針程序的朋友應(yīng)該會記得, 使用 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è)多帶帶的目錄下, 方便設(shè)置 PYTHONPATH 后不會有其他的干擾(代碼可以從 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.py
import _hook

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

import os
import sys

current_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 解釋器程序的絕對路徑 ``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.py
import sys
import hello

print(sys.argv)
print(hello.sleep(3))

使用方法:

$ python agent.py test.py arg1 arg2
find_module usercustomize
find_module hello
load_module hello
["test.py", "arg1", "arg2"]
start func
spent 3.005035161972046s
3

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

如果大家對商用探針程序的具體實(shí)現(xiàn)感興趣的話,可以看一下國外的 New Relic 或國內(nèi)的 OneAPM, 聽云 等這些 APM 廠商的商用 python 探針的源代碼。

P.S. 本文首發(fā)于 我的博客 ;)
P.S. 本文涉及的代碼可以從 Github 上獲取 ;)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/37868.html

相關(guān)文章

  • Python精選閱讀 0x01期

    摘要:本文講述了各種針對的方案比如和,尤其是針對等科學(xué)計(jì)算庫的化的進(jìn)展與困擾。本文認(rèn)為科學(xué)計(jì)算的未來必定會大規(guī)模的引用以提升效率。上相關(guān)的討論見這里。英文版本見郵件訂閱精選閱讀 專題:Python的各種黑魔法 用各種generator/iterator/descriptor等黑魔法,加上各種函數(shù)編程方法的使用,Python總能使用很短的代碼完成很復(fù)雜的事情,下面集中放一些這方面的文章 知乎...

    nicercode 評論0 收藏0
  • newrelic python agent 源碼分析-1

    摘要:是應(yīng)用性能管理監(jiān)控解決方案提供商。目錄是列出的命令腳本所在目錄。包含文件如下的函數(shù)是命令執(zhí)行的入口。而對于硬件信息的檢測則由進(jìn)行。文檔地址源碼仔細(xì)看下去,太復(fù)雜了。下一篇再分析一個(gè)請求到結(jié)束探針工作的完整過程吧。 Newrelic 是APM(Application Performance Management)(應(yīng)用性能管理/監(jiān)控)解決方案提供商。項(xiàng)目中,通常用它來追蹤應(yīng)用的性能。最近...

    szysky 評論0 收藏0
  • 如何在 virtualenv 環(huán)境下搭建 Python Web

    摘要:生產(chǎn)環(huán)境下,自帶的服務(wù)器,無法滿足性能要求。配置前面我們已經(jīng)在系統(tǒng)環(huán)境下安裝了安裝好的二進(jìn)制文件放在文件夾下,接下來使用來管理。參考文章探針安裝部署部署筆記在生產(chǎn)環(huán)境上部署使用詳解本文系工程師編譯整理。 由于字?jǐn)?shù)的限制,其實(shí)本篇文章的全標(biāo)題為 《如何在 virtualenv 環(huán)境下 Django + Nginx + Gunicorn+ Supervisor 搭建 Python Web》...

    roland_reed 評論0 收藏0
  • 如何在 virtualenv 環(huán)境下搭建 Python Web

    摘要:生產(chǎn)環(huán)境下,自帶的服務(wù)器,無法滿足性能要求。配置前面我們已經(jīng)在系統(tǒng)環(huán)境下安裝了安裝好的二進(jìn)制文件放在文件夾下,接下來使用來管理。參考文章探針安裝部署部署筆記在生產(chǎn)環(huán)境上部署使用詳解本文系工程師編譯整理。 由于字?jǐn)?shù)的限制,其實(shí)本篇文章的全標(biāo)題為 《如何在 virtualenv 環(huán)境下 Django + Nginx + Gunicorn+ Supervisor 搭建 Python Web》...

    godiscoder 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<