Django框架的丝般顺滑性能分析
项目描述
Silk
Silk是一个为Django框架提供的实时性能分析工具。Silk拦截并存储HTTP请求和数据库查询,然后将它们以用户界面形式呈现,以便进一步检查。
内容
要求
Silk已在以下环境中测试过:
- Django: 3.2, 4.2, 5.0, 5.1
- Python: 3.8, 3.9, 3.10, 3.11, 3.12
安装
通过pip安装到virtualenv
pip install django-silk
在settings.py
中添加以下内容
MIDDLEWARE = [
...
'silk.middleware.SilkyMiddleware',
...
]
INSTALLED_APPS = (
...
'silk'
)
注意:中间件的位置很敏感。如果在silk.middleware.SilkyMiddleware
之前的中间件在process_request
中返回,则SilkyMiddleware
将永远不会有机会执行。因此,您必须确保放置在之前的任何中间件都不会从process_request
返回任何内容。有关此信息的更多信息,请参阅Django文档。
注意:如果您正在使用django.middleware.gzip.GZipMiddleware
,请将其放置在silk.middleware.SilkyMiddleware
之前,否则您将遇到编码错误。
如果您想使用自定义中间件,例如您开发了silk.middleware.SilkyMiddleware
的子类,那么您可以使用以下设置组合
# Specify the path where is the custom middleware placed
SILKY_MIDDLEWARE_CLASS = 'path.to.your.middleware.MyCustomSilkyMiddleware'
# Use this variable in list of middleware
MIDDLEWARE = [
...
SILKY_MIDDLEWARE_CLASS,
...
]
要启用对用户界面的访问,请将以下内容添加到您的urls.py
urlpatterns += [path('silk/', include('silk.urls', namespace='silk'))]
在运行迁移之前
python manage.py migrate
python manage.py collectstatic
Silk将自动开始拦截请求,并且您可以继续添加分析(如果需要的话)。UI可以通过/silk/
访问
替代安装
通过GitHub标签
pip install https://github.com/jazzband/silk/archive/<version>.tar.gz
您可以使用以下命令从master安装,但请注意,master中的版本可能不会适用于在要求中指定的所有版本
pip install -e git+https://github.com/jazzband/django-silk.git#egg=django-silk
功能
Silk主要由以下部分组成:
- 拦截请求/响应的中间件
- 对SQL执行进行封装,以分析数据库查询
- 用于手动或动态分析代码块和函数的上下文管理器/装饰器。
- 用于检查和可视化上述内容的用户界面。
请求检查
Silk中间件会拦截并存储在配置的数据库中的请求和响应。然后可以通过请求概述使用Silk的UI过滤和检查这些请求
它记录如下内容:
- 耗时
- 查询数
- 查询耗时
- 请求/响应头
- 请求/响应体
等等。
通过单击相关的请求还可以获得每个请求的更多详细信息
SQL检查
Silk还会拦截由每个请求生成的SQL查询。我们可以总结有关涉及到的表、连接数和执行时间(可以通过单击列标题对表进行排序)的内容
在深入堆栈跟踪以确定此请求的来源之前
分析
启用SILKY_PYTHON_PROFILER设置以使用Python内置的cProfile
分析器。每个请求都将单独进行分析,分析器的输出将可在Silk UI的请求分析页面中找到。请注意,从Python 3.12开始,cProfile
无法并发运行,因此Python 3.12及更高版本的django-silk在另一个分析器正在运行时将不会进行分析(即使是在另一个线程中的自己的分析器)。
SILKY_PYTHON_PROFILER = True
如果您想生成二进制.prof
文件,请设置以下内容
SILKY_PYTHON_PROFILER_BINARY = True
启用时,将使用gprof2dot和viz.js生成的图形可视化显示在分析详细页面中
可以使用自定义存储类来保存生成的二进制.prof
文件
# For Django >= 4.2 and Django-Silk >= 5.1.0:
# See https://docs.django.ac.cn/en/5.0/ref/settings/#std-setting-STORAGES
STORAGES = {
'SILKY_STORAGE': {
'BACKEND': 'path.to.StorageClass',
},
# ...
}
# For Django < 4.2 or Django-Silk < 5.1.0
SILKY_STORAGE_CLASS = 'path.to.StorageClass'
默认存储类是silk.storage.ProfilerResultStorage
,当使用它时,您可以指定您选择的路径。您必须确保指定的目录存在。
# If this is not set, MEDIA_ROOT will be used.
SILKY_PYTHON_PROFILER_RESULT_PATH = '/path/to/profiles/'
每个请求都将有一个下载按钮,下载二进制.prof
文件。此文件可用于使用snakeviz或其他cProfile工具进行进一步分析
要检索生成特定配置文件端点的方法,可以在文件名中添加请求路径的占位符,如下所示
SILKY_PYTHON_PROFILER_EXTENDED_FILE_NAME = True
Silk 还可以用于分析特定的代码块或函数。为此,它提供了一个装饰器和上下文管理器。
例如
from silk.profiling.profiler import silk_profile
@silk_profile(name='View Blog Post')
def post(request, post_id):
p = Post.objects.get(pk=post_id)
return render(request, 'post.html', {
'post': p
})
每当查看博客文章时,我们都会在 Silk UI 中获得一个条目
Silk 分析不仅提供执行时间,而且还以与请求相同的方式收集块内执行的 SQL 查询
装饰器
Silk 装饰器可以应用于函数和方法
from silk.profiling.profiler import silk_profile
# Profile a view function
@silk_profile(name='View Blog Post')
def post(request, post_id):
p = Post.objects.get(pk=post_id)
return render(request, 'post.html', {
'post': p
})
# Profile a method in a view class
class MyView(View):
@silk_profile(name='View Blog Post')
def get(self, request):
p = Post.objects.get(pk=post_id)
return render(request, 'post.html', {
'post': p
})
上下文管理器
使用上下文管理器意味着我们可以向名称中添加额外的上下文,这对于缩小缓慢到特定数据库记录非常有用。
def post(request, post_id):
with silk_profile(name='View Blog Post #%d' % self.pk):
p = Post.objects.get(pk=post_id)
return render(request, 'post.html', {
'post': p
})
动态分析
Silk 的一个更有趣的特性是动态分析。例如,如果我们只想分析我们只有只读访问权限的依赖项中的函数(例如,由 root 拥有的系统 Python 库),我们可以在 settings.py
中添加以下内容以在运行时应用装饰器
SILKY_DYNAMIC_PROFILING = [{
'module': 'path.to.module',
'function': 'MyClass.bar'
}]
这大致相当于
class MyClass:
@silk_profile()
def bar(self):
pass
以下总结了可能的情况
"""
Dynamic function decorator
"""
SILKY_DYNAMIC_PROFILING = [{
'module': 'path.to.module',
'function': 'foo'
}]
# ... is roughly equivalent to
@silk_profile()
def foo():
pass
"""
Dynamic method decorator
"""
SILKY_DYNAMIC_PROFILING = [{
'module': 'path.to.module',
'function': 'MyClass.bar'
}]
# ... is roughly equivalent to
class MyClass:
@silk_profile()
def bar(self):
pass
"""
Dynamic code block profiling
"""
SILKY_DYNAMIC_PROFILING = [{
'module': 'path.to.module',
'function': 'foo',
# Line numbers are relative to the function as opposed to the file in which it resides
'start_line': 1,
'end_line': 2,
'name': 'Slow Foo'
}]
# ... is roughly equivalent to
def foo():
with silk_profile(name='Slow Foo'):
print (1)
print (2)
print(3)
print(4)
请注意,动态分析的行为与 Python 模拟框架相似,即我们修改函数本身,例如
""" my.module """
from another.module import foo
# ...do some stuff
foo()
# ...do some other stuff
,我们会通过动态装饰 my.module.foo
来分析 foo
,而不是 another.module.foo
SILKY_DYNAMIC_PROFILING = [{
'module': 'my.module',
'function': 'foo'
}]
如果我们要将动态分析应用于在已导入后导入的函数源模块 another.module.foo
,则不会触发分析。
分析的自定义逻辑
有时您可能希望动态控制分析器何时运行。您可以为何时启用分析器编写自己的逻辑。为此,请将以下内容添加到您的 settings.py
此设置与 SILKY_PYTHON_PROFILER 相互排斥,并且如果存在,将覆盖它。它将与 SILKY_DYNAMIC_PROFILING 一起使用。
def my_custom_logic(request):
return 'profile_requests' in request.session
SILKY_PYTHON_PROFILER_FUNC = my_custom_logic # profile only session has recording enabled.
您还可以使用一个 lambda
表达式。
# profile only session has recording enabled.
SILKY_PYTHON_PROFILER_FUNC = lambda request: 'profile_requests' in request.session
代码生成
Silk 当前为每个请求生成两段代码
这两段代码都旨在用于重新播放请求。可以使用 curl 命令通过命令行进行重播,也可以在 Django 单元测试或作为独立脚本中使用 Python 代码。
配置
身份验证/授权
默认情况下,任何人都可以通过转到 /silk/
访问 Silk 用户界面。要启用您的 Django 身份验证后端,请在 settings.py
中添加以下内容
SILKY_AUTHENTICATION = True # User must login
SILKY_AUTHORISATION = True # User must have permissions
如果 SILKY_AUTHORISATION
设置为 True
,则默认情况下 Silk 将只授权将 is_staff
属性设置为 True
的用户。
您可以使用以下内容在 settings.py
中自定义此设置
def my_custom_perms(user):
return user.is_allowed_to_use_silk
SILKY_PERMISSIONS = my_custom_perms
您还可以使用一个 lambda
表达式。
SILKY_PERMISSIONS = lambda user: user.is_superuser
请求/响应体
默认情况下,Silk 会为每个请求保存请求和响应体,以便将来查看,无论大小如何。如果在生产中大量使用 Silk 且请求体很大,这可能会对空间/时间性能产生巨大影响。此行为可以通过以下选项进行配置
SILKY_MAX_REQUEST_BODY_SIZE = -1 # Silk takes anything <0 as no limit
SILKY_MAX_RESPONSE_BODY_SIZE = 1024 # If response body>1024 bytes, ignore
元性能分析
有时了解 Silk 对请求/响应时间的影响很有用。为此,请将以下内容添加到您的 settings.py
SILKY_META = True
Silk 将记录在每次请求结束时将所有内容保存到数据库所需的时间
请注意,在上面的屏幕截图中,这意味着请求耗时 29ms(其中 Django 耗时 22ms,Silk 耗时 7ms)
记录部分请求
在高负载网站上,可能只需记录部分请求。为此,请将以下内容添加到您的 settings.py
注意:此设置与 SILKY_INTERCEPT_FUNC 相互排斥。
SILKY_INTERCEPT_PERCENT = 50 # log only 50% of requests
记录请求的自定义逻辑
在高负载网站上,编写自己的逻辑来拦截请求可能也有帮助。为此,请将以下内容添加到您的 settings.py
注意:此设置与 SILKY_INTERCEPT_PERCENT 不兼容。
def my_custom_logic(request):
return 'record_requests' in request.session
SILKY_INTERCEPT_FUNC = my_custom_logic # log only session has recording enabled.
您还可以使用一个 lambda
表达式。
# log only session has recording enabled.
SILKY_INTERCEPT_FUNC = lambda request: 'record_requests' in request.session
限制请求/响应数据
为了确保 Silk 能够回收旧请求/响应数据,可以设置一个配置变量来限制它存储的请求/响应行数。
SILKY_MAX_RECORDED_REQUESTS = 10**4
垃圾回收只针对请求的百分比运行,以减少开销。可以通过此配置进行调整
SILKY_MAX_RECORDED_REQUESTS_CHECK_PERCENT = 10
如果您想将 Silk 的垃圾回收与您的 web 服务器请求处理解耦,请设置 SILKY_MAX_RECORDED_REQUESTS_CHECK_PERCENT=0 并手动触发,例如在 cron 作业中
python manage.py silk_request_garbage_collect
启用查询分析
如果 dbms 支持,可以通过设置配置变量来执行带有分析功能的查询。
SILKY_ANALYZE_QUERIES = True
警告:此设置可能导致数据库执行相同的查询两次,具体取决于后端。例如,Postgres 中的 EXPLAIN ANALYZE
实际上会执行查询,这可能导致意外的数据更新。请谨慎设置此为 True。
如果您想为支持 dbms 的配置传递额外的参数(例如,VERBOSE、FORMAT JSON),可以按照以下方式进行。
SILKY_EXPLAIN_FLAGS = {'format':'JSON', 'costs': True}
在请求体上遮蔽敏感数据
默认情况下,Silk 会过滤包含以下键的值(它们不区分大小写)
SILKY_SENSITIVE_KEYS = {'username', 'api', 'token', 'key', 'secret', 'password', 'signature'}
但有时,您可能希望有自己的敏感关键词,那么上述配置可以修改
SILKY_SENSITIVE_KEYS = {'custom-password'}
清除记录的数据
管理命令会清除所有记录的数据
python manage.py silk_clear_request_log
贡献
这是一个 Jazzband 项目。通过贡献,您同意遵守 贡献者行为准则 并遵循 指南。
开发环境
Silk 包含一个名为 project
的项目,可用于 silk
开发。它将 silk
代码链接,因此您可以在同一时间工作于示例 project
和 silk
包。
为了设置本地开发,您首先需要安装测试 project
的所有依赖项。从 project
目录的根目录开始
pip install -r requirements.txt
您还需要安装 silk
的依赖项。从 git 仓库的根目录开始
pip install -e .
此时,您的虚拟环境应该拥有运行示例 project
和 silk
所需的一切。
在运行之前,您必须设置 DB_ENGINE
和 DB_NAME
环境变量
export DB_ENGINE=sqlite3
export DB_NAME=db.sqlite3
对于其他组合,请检查 tox.ini
。
现在从示例 project
的根目录应用迁移
python manage.py migrate
现在从示例 project
目录的根目录启动 django 服务器
python manage.py runserver
运行测试
cd project
python manage.py test
快乐的剖析!
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。