阶段三-06 大规模优化

BUG处理

日志读写bug:

异步运行时 celery worker和django runserver都会去操作日志,导致错误 【win error 32】多进程无法同时访问

beat 清理bug:

之前我们自定的方法run_cases_periodic 的 periodic_task_id 导致了beat本身的 celery.backend_cleanup 接受了一个多余参数 periodic_task_id,所以报错

class TestPltModelEntry(schedulers.ModelEntry):
    """
    继承并自定义ModelEntry,重写部分构造函数
    """
    def __init__(self, model, app=None):
        """
        指定以_periodic结尾的分发,向task传递periodic_task_id(作为keyword命名参数)
        :param model:
        :param app:
        """
        super().__init__(model, app)
        if model.task.endswith('_periodic'):
            self.kwargs['periodic_task_id'] = model.pk

性能优化

经典ORM N+1问题

套件运行多次后会产生很多条接口履历,我们进入到接口履历页面查看时可能会等待很久才能加载完成(2500条大概需要24S)。

假设列表页要把2500条履历展示出来,django会用QuerySet( 而 QuerySet 本质上是通过在预先定义好的 model 中的 Manager 和数据库进行交互。)转换成sql去查数据库;我们结合django-debug-toolbar来查看

先pip,再setting导入INSTALLED_APPS = 'debug_toolbar',一定要把debug_toolbar放在django.contrib.staticfiles后边。

import debug_toolbar
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('__debug__/', include(debug_toolbar.uris)),
]

目的是前端访问 /debug时 转发到 debug_toolbar.uris 中

接下来导入中间键

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',]

N+1问题是指,只要存在主从关系的情况(存在外键关联),用testbatch测试批次举例:ORM的思路是进入页面(每页10条数据)首先会查testbatch主表有几条数据,然后根据主表中的要展示的关联字段的外键去一条条查询对应表的数据。

1可以理解为查询testbatch的1条sql语句;N代表testbatch主表有多条数据就执行N条sql

select查询了表的所有字段

在配合debug工具接口履历中看下,查询了这几个管理表的所有字段了,和 *from没有区别了。

我们使用 list_select_related = ('api',) ;填上需要的关联,消减掉不必要的表关联查询

only() ;用于消减不必要的字段。

优化后发现耗时只少了一点,进入他的查询计划,耗时的大头应该是在临时表、order by之后的filesort(磁盘执行/内存执行)

看下my.ini文件中,innodb_buffer_pool_size:表示缓存的大小,InnoDB 使用一个缓冲池类保存索引和原始数据。改大一点查看,改成500M重启试试快多了。一般百万级千万级的数据需要8-16G或更多内存分配,要结合业务,在生产环境中最好一步到位。

关于Djano的缓存

django可以实现:页面缓存(站点缓存、视图缓存)、数据库查询结果缓存、通用缓存(底层缓存API)、django-redis缓存

迁移/整合,升级django版本

https://docs.djangoproject.com/zh-hans/4.1/topics/migrations/#squashing-migrations

官网中展示了用法:

manage.py squashmigrations app名 00XX
pip list # 查看版本
pip install -U --upgrade #升级所有包

国际化和翻译

由于celery-result没有提供国际化翻译的文件,所以我们用官方推荐工具翻译

首先在 settings里写入 LOCALE_PATHS = (BASE_DIR / 'locale/',) 之后运行python manage makemessages -l zh_Hans ;会在项目根目录创建一个locale文件夹

钉钉群通知

通过钉钉机器人webhook实现,比较简单。先在群中创建自定义机器人,然后使用加签和关键字。在settings中配置

DINGTALK_WEB_HOOK = 'https://oapi.dingtalk.com/robot/send?access_token=' + token
DINGTALK_WEB_HOOK_SING = '加签'

在APP test_plt中utlis下创建dingtalk.py

from dingtalkchatbot.chatbot import DingtalkChatbot
from django.conf import settings

DINGTALK_TEXT_TMPL_API_TASK = '[接口测试任务完成通知]\n%s。]'


def send_text(message, at_mobiles=[], tmpl=''):
    # 引用 settings里m配置的钉钉群消息通知的WebHook地:
    web_hook = settings.DINGTALK_WEB_HOOKsiqn = settings.DINGTALK_WEB_HOOK_SIGN
    sign = settings.DINGTALK_WEB_HOOK_SIGN
    if not web_hook:
        return

    # 初始化机器人
    chatbot = DingtalkChatbot(web_hook) if not sign else DingtalkChatbot(web_hook, secret=sign)
    # Text消&@所有人
    msg = message if not tmpl else tmpl % message
    chatbot.send_text(msg=msg, at_mobiles=at_mobiles)

我们想要执行套件后就发送消息,在task.run_suites return前调用即可:

# 调用钉钉通知函数;msg:传入TestBatch repr(和__str__类似)详情 , tml:传入我们配置的模版
    dingtalk.send_text(repr(bat), tmpl=dingtalk.DINGTALK_TEXT_TMPL_API_TASK)

Django换肤

基于现在的页面 UI/UE/UX都不太便捷美观。

关于页面original展示错误

ID展示乱码问题 TODO

前端页面继承优化,由于我们要优化admin的页面所有在项目根目录下创建 templates.admin 文件架管理进settings:

该页面基础自django的base_site.html:

{% extends "admin/base_site.html" %}

{% block extrahead %}
<style>
    .inline-group .tabular td.original p {
    position: absolute;
    left: 0;
    height: 1.5em;
    padding: 2px 9px;
    overflow: hidden;
    font-size: 0.5625rem;
    font-weight: bold;
    color: var(--body-quiet-color);
    _width: 700px;
}
</style>
{% endblock %}

admin app和model排序

在setting同级目录下创建 admin.py apps.py

# admin.py 继承原生使用name属性排序,修改为自己写死
from django.contrib.admin import AdminSite


class TestPltAdminSite(AdminSite):
    def get_app_list(self, request, app_label=None):
        app_ordering = {
            '测试平台': 1,
            'Celery Results': 2,
            '周期任务': 3,
            '认证和授权': 4
        }
        # 获取所有的app加入字典
        app_dict = self._build_app_dict(request, app_label)

        model_orderiny = {
            '测试项目': 1,
            '接口定义': 2,
            '接口执行履历': 3,
            '用例': 4,
            '用例执行履历': 5,
            '用例套件': 6,
            '用例套件执行履历': 7,
            '测试批次(报告)': 8,
            '用例接口': 9,
            '测试成员': 10,
            '部署环境': 11
        }
        # 对app排序
        app_list = sorted(app_dict.values(), key=lambda x: app_ordering[x["name"]])

        # 遍历app中的model排序
        for app in app_list:
            if app["name"] == '测试平台':
                app["models"].sort(key=lambda x: model_orderiny[x["name"]])

            else:
                app["models"].sort(key=lambda x: x["name"])

        return app_list
# apps.py
from django.contrib.admin.apps import AdminConfig


class TestPltAdminConfig(AdminConfig):
    default_site = 'djangoProject1.admin.TestPltAdminSite'

在setting中注册 'djangoProject1.apps.TestPltAdminConfig', 并注释掉原本的 'django.contrib.admin',

点击内联表中的ID跳转相应页面

举个例子,点击测试批次报告中的接口内联表ID想要跳转到该条接口执行履历。这些一直没有实现,这里查看NestedTabularInline 的属性后决定用 多继承来解决

多继承;一个类继承多个基类:C++、python支持,Java不支持但是通过其他方法实现;Java 基类+接口,extends Clz implements Interface1

from django.utils.safestring import mark_safe ; 安全的写入方法,不用这个方法会被框架自动转码

class InlineIdLinkMixin:
    @admin.display(description='ID')
    def id_link(self, obj):
        opts = self.model._meta
        uri = reverse(f'admin:{opts.app_label}_{opts.object_name.lower()}_change', args=[obj.pk])
        return mark_safe(f'<a href="{uri}" target="_blank">{obj.id}</a>')

需要使用id_link方法的内联直接继承,之后还需要加上 readonly_fields = ('id_link',)

跟换django-simpleui

导入包django-simpleui(开源版)INSTALLED_APPS中注册simpleui。

ui上有不匹配的地方需要更改下,之前admin原皮肤中定义的失效了

我们直接不要左上角的注释了,把它间距取低一点

希望限制宽度,利用td class来限制(写死)

{% extends "admin/base_site.html" %}

{% block extrahead %}
<style>
    td.original p {
        visibility: hidden;
}
    .inline-group .tabular tr.has_original td {
        padding-top: 5px;
}
    .field-request_headers, .field-request_body, .field-response_headers, .field-response_body{
    word-break: break-all;
    max-width: 250px;
}
</style>
{% endblock %}

settings中配置部分simpleui的属性

# 面向源代码(开发)
STATICFILES_DIRS = [
    BASE_DIR / 'shared_static'
]

# 面向部署的URL和静态资源
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'static'

# ######### simpleui #########
SIMPLEUI_LOGO = '/static/tmr.jpg'
SIMPLEUI_HOME_INFO = FalseSIMPLEUI_ANALYSIS = False
SIMPLEUI_DEFAULT_THEME = 'simpleui.css'
SIMPLEUI_HOME_QUICK = True
SIMPLEUI_HOME_ACTION = True

把需要的图片或静态文件放到项目根目录下的shared_static中,需要注意的是DEBUG和部署生产运行是不同的工作状态:

Django看待静态文件:新闻网站(图片、视频、音频)静态文件的独立性,能被独立部署到CDN

Django会提供python manage.py collectstatic
把整个项目所有的静态资源(还包括js、css),归集到一起 (STATIC ROOT)开发时,分app开发,每个app下可能有静态资源?还有全局的静态资源?
STATICFILES DIRS是全局的静态资源配置,可以指定每个app的静态资源,和共享的静态资源;collectstatic时,会扫描STAPICEFILES DIRS下的静态资源,以及所有注册的app下的静态资源;
注意:当STATICEILES DIRS配置的路径下,存在和app下同路径资源时,会覆盖app的资源;
最后部署时,static需要单独部署,Django通过STATIC URL来找到这些资源;
DEBUG状态通过runserver启动Django时,Django会自动处理静态资源,不需要执行collectstatic;

需要换图标请配置 SIMPLEUI_CONFIG ;需要跟换可以搜索 font awesome 复制icon名之后F12在页面上替换icon试下,能用就说明版本支持

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值