newrelic python agent 源码分析-1

Newrelic 是APM(Application Performance Management)(应用性能管理/监控)解决方案提供商。项目中,通常用它来追踪应用的性能。最近看了一下 newrelic-python-agent 源码,这是查看源码过程中的一些记录。

目录结构

newrelic 目录结构如下:

newrelic
├── admin  # 常用命令
├── api    # 探针
├── bootstrap
├── common  
├── core
├── extras
│   └── framework_django
│       └── templatetags
├── hooks   # 数据库 web 各个库的一些探针
│   ├── framework_tornado
│   ├── framework_tornado_r3
│   └── framework_tornado_r4
├── network
├── packages
│   ├── requests
│   │   └── packages
│   │       ├── chardet
│   │       └── urllib3
│   │           ├── packages
│   │           │   └── ssl_match_hostname
│   │           └── util
│   └── wrapt
└── samplers
复制代码

命令

使用 newrelic-admin help 可以列出所有命令:

$ newrelic-admin help

Usage: newrelic-admin command [options]

Type 'newrelic-admin help <command>'for help on a specific command.

Available commands are:
  generate-config
  license-info
  license-key
  local-config
  network-config
  record-deploy
  run-program
  run-python
  server-config
  validate-config
复制代码

通过 setup.py 代码可以知道:

if with_setuptools:
    kwargs['entry_points'] = {
            'console_scripts': ['newrelic-admin = newrelic.admin:main'],
            }
复制代码

newrelic-admin 命令调用的是 newrelic.admin:main,这是代码的入口。首先看一下 newrelic/admin/目录。

admin

admin 目录是 newrelic-admin help 列出的命令脚本所在目录。

包含文件如下:

$ tree
admin
├── __init__.py
├── __main__.py
├── debug_console.py
├── generate_config.py
├── license_info.py
├── license_key.py
├── local_config.py
├── network_config.py
├── record_deploy.py
├── run_program.py
├── run_python.py
├── server_config.py
└── validate_config.py
复制代码

__init__.py 的 main 函数 是命令执行的入口。

__init__.py 文件中代码

load_internal_plugins()
load_external_plugins()
复制代码

用来加载 _builtin_plugins 中定义的命令。

run_program

首先看下 run_program 命令,这个命令使用方式如下:

newrelic-admin run-program your command
复制代码

newrelic/admin/run_program.pyrun_program 函数有装饰器 command,用来定义将命令以及相关说明添加到字典 _commands

run_program 中代码:

root_directory = os.path.dirname(root_directory)
boot_directory = os.path.join(root_directory, 'bootstrap')

if 'PYTHONPATH' in os.environ:
    path = os.environ['PYTHONPATH'].split(os.path.pathsep)
    if not boot_directory in path:
        python_path = "%s%s%s" % (boot_directory, os.path.pathsep, os.environ['PYTHONPATH'])

os.environ['PYTHONPATH'] = python_path
复制代码

可以发现newrelic/bootstrap/sitecustomize.py 文件被加入到了 PYTHONPATH。

python 解释器初始化的时候会自动 import PYTHONPATH 下存在的 sitecustomizeusercustomize 模块。

之后的功能比较简单,就是调用 os 模块执行命令。

现在看下newrelic/bootstrap/sitecustomize.py 代码。

在 这个文件的最后一行:

newrelic.config.initialize(config_file, environment)
复制代码

这里用来初始化newrelic,具体代码在 newrelic/config.py文件。

以下是initialize函数:

def initialize(config_file=None, environment=None, ignore_errors=None,
            log_file=None, log_level=None):

    if config_file is None:
        config_file = os.environ.get('NEW_RELIC_CONFIG_FILE', None)

    if environment is None:
        environment = os.environ.get('NEW_RELIC_ENVIRONMENT', None)

    if ignore_errors is None:
        ignore_errors = newrelic.core.config._environ_as_bool(
                'NEW_RELIC_IGNORE_STARTUP_ERRORS', True)

    _load_configuration(config_file, environment, ignore_errors,
            log_file, log_level)  # 加载配置

    if _settings.monitor_mode or _settings.developer_mode:
        _settings.enabled = True
        _setup_instrumentation()  # 设置探针
        _setup_data_source()  # TODO
        _setup_extensions()   # TODO
        _setup_agent_console()   # TODO
    else:
        _settings.enabled = False
复制代码

其中第14行 _load_configuration 是用来加载 newrelic 的相关配置。比如:日志目录、各种环境变量、秘钥、newrelic host 地址等等。

`_setup_instrumentation() 中 _process_module_builtin() 用来设置探针。

数据库、外部请求 等监控模块都位于 hook 目录下,通过 _process_module_builtin 函数将进程与监控模块进行绑定,包括 django 的主要模块以及常用的数据库等。在核心模块执行的时候触发监控,将数据回传到 api.time_trace 模块进行处理。

而对于硬件信息的检测则由 commo.system_info 进行。

newrelic run_program 初始化过程

以下为 flask 应用初始化过程,其它应用类似:

  1. newrelic/admin/__init__.py main()
  2. newrelic/admin/run_program.py 代码中会把 newrelic/bootstrap/sitecustomize.py 添加到 PYTHONPATH,python 解释器初始化的时候会自动 import PYTHONPATH 下存在的 sitecustomizeusercustomize 模块
  3. newrelic/bootstrap/sitecustomize.py 调用 newrelic.config.initialize()_setup_instrumentation() 函数被调用,_process_module_builtin会把需要 wrap 的包先添加到_import_hooks。
  4. newrelic/config.pysys.meta_path.insert(0, newrelic.api.import_hook.ImportHookFinder()) 执行
  5. newrelic/api/import_hook.py ImportHookFinder().find_model()
  6. newrelic/api/import_hook.py _ImportHookLoader() or _ImportHookChainedLoader()
  7. newrelic/api/import_hook.py _notify_import_hooks callablenewrelic/config _module_import_hook _instrument
  8. newrelic/hooks/framework_flask.py instrument_flask_app
  9. newrelic/api/web_transaction.py wrap_wsgi_application
  10. newrelic/common/object_wrapper.py wrap_object

在代码中,使用到了第三方包 wrapt,以下是 wrapt 的官方描述(文档地址)。

wrapt模块的目的是为Python提供一个透明的对象代理,它可以作为构建函数包装器和装饰函数的基础。wrapt 提供了一个简单易用的decorator工厂,利用它你可以简单地创建decorator,并且在任何情况下都可以正确地使用它们。

wrapt简单示例如下:

import wrapt
# 普通装饰器
@wrapt.decorator
def pass_through(wrapped, instance, args, kwargs):
    return wrapped(*args, **kwargs)

@pass_through
def function():
    pass

# 带参数的装饰器
import wrapt

def with_arguments(myarg1, myarg2):
    @wrapt.decorator
    def wrapper(wrapped, instance, args, kwargs):
        return wrapped(*args, **kwargs)
    return wrapper

@with_arguments(1, 2)
def function():
    pass
复制代码

要实现decorator,需要首先定义一个装饰器函数。这将在每次调用修饰函数时调用。装饰器函数需要使用四个位置参数:

  • wrapped - The wrapped function which in turns needs to be called by your wrapper function.
  • instance - The object to which the wrapped function was bound when it was called.
  • args - The list of positional arguments supplied when the decorated function was called.
  • kwargs - The dictionary of keyword arguments supplied when the decorated function was called.

具体使用参考文档吧。 文档地址


newrelic 源码仔细看下去,太...复杂了。下一篇再分析一个 flask 请求到结束探针工作的完整过程吧。

如果下一篇一直没写,那应该是看不下去了。

最后,感谢女朋友支持和包容,比❤️

也可以在公号输入以下关键字获取历史文章:公号&小程序 | 设计模式 | 并发&协程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Python爬虫中,可以通过伪装User-Agent来隐藏爬虫的真实身份,避免被网站封禁。User-Agent是HTTP请求头的一部分,用于描述客户端的类型、操作系统、浏览器等信息。一些网站会根据User-Agent来识别客户端的真实身份,所以我们可以通过修改User-Agent来达到伪装的目的。 在Python中,可以使用第三方库如Requests、urllib等来发送HTTP请求,并通过设置headers参数来伪装User-Agent。具体来说,我们可以在headers中设置User-Agent字段,例如: ``` import requests url = 'http://example.com' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36' } response = requests.get(url, headers=headers) ``` 在上述代码中,我们设置了User-Agent为Chrome浏览器的信息,这样就可以伪装成浏览器发送HTTP请求,而不是Python爬虫发送的请求。当然,为了避免被网站封禁,我们需要经常更新User-Agent,同时也要注意不要过于频繁地发送请求,以免被识别为爬虫。 ### 回答2: Python爬虫伪装User-Agent指的是开发者在编写Python代码的过程中,将User-Agent设置为浏览器端的User-Agent,从而避免服务器端针对爬虫进行封禁或者限制访问。 User-Agent是HTTP请求的头部信息之一,用于告知服务器客户端正在使用的浏览器和操作系统的情况。由于爬虫使用的请求头往往与常规浏览器的请求头不同,因此服务器可能会对其进行限制、封禁甚至是先后回应。 为了避免这种情况,我们可以在代码中设置一个随机的User-AgentPython中有许多第三方库提供了User-Agent,比如fake_useragent、user_agents等。这些库可以生成一个随机的User-Agent,让服务器无法将其与爬虫识别出来。 另外,我们也可以手动编写每个请求的User-Agent,使其更加随机和多变。在编写代码时,我们可以将User-Agent列表存储到文件中,然后在每次请求时随机选择一个User-Agent,使得每个请求使用的User-Agent都不相同。 需要注意的是,虽然伪装User-Agent可以一定程度上避免被服务器封禁,但过于频繁的访问依然可能被服务器识别为爬虫行为。因此,建议在爬虫程序中加入适当的延时和限制访问次数的机制,以避免被反爬虫机制限制。 ### 回答3: 为了更好地伪装自己的爬虫程序,我们需要理解什么是 User-Agent。User-Agent 是一种 HTTP 请求头部信息,用于描述用户代理的一些信息,包括操作系统、浏览器类型、版本等等。在爬虫请求时,服务器通过 User-Agent 来判断请求是否来自浏览器。因此,如果我们的爬虫请求头中不带 User-Agent 或者 User-Agent 显然是一个机器人,那爬虫很可能会被服务器封禁。 通过 Python 爬虫伪装 User-Agent 的方法有很多种,以下是一些常用的方法: - 使用 fake-useragent:fake-useragent 是一个第三方库,可以自动生成随机 User-Agent,使用方法非常简单。 - 使用 requests 库自定义 headers:这种方法的灵活度比较高,可以自定义 User-Agent,还可以添加其他自定义信息。 - 使用 random.choice() 函数随机选择 User-Agents:可以通过准备好多个 User-Agent 列表,然后随机选择一个 User-Agent。 参考下面代码: ``` import requests import random # 使用fake_useragent库来设置User-Agent from fake_useragent import UserAgent # 使用random.choice随机选择User-Agent UserAgents = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", ] # 设置随机User-Agent headers = { 'User-Agent': random.choice(UserAgents) } # 或使用fake-useragent库来生成User-Agent headers = { 'User-Agent': UserAgent().random } # 执行请求 response = requests.get(url, headers=headers) ``` 总之,为了避免被服务器封禁,我们需要用一些方法伪装自己的 User-Agent,使其看起来更像是来自浏览器的请求。当然,我们也需要注意不要伪装得过于真实,否则过多的请求可能会对目标网站造成负面影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值