前言

最近因为政企部门的工作失误,导致我们的项目差点挂掉,客户意见很大,然后我们只能被动进入007加班状态,忙得嗷嗷叫,直到今天才勉强把项目改完交付,是时候写一个小结。

技术

因为前期需求不明确,数据量不大,人手也不多,所以我直接用Django做了后端,Django自带的admin可以作为管理后台使用,可以很快完成这个需求。

我们的前端有两个,一个数据展示大屏,一个可视化地图。前者使用Vue+ElementUI+DataV实现,后者使用jQuery+百度MapV。

大概的效果如下所示,涉及到数据的部分只能打码,感谢理解~

这个是Django的admin界面,主页是我重新写的

数据展示大屏

可视化地图

技术含量其实不高,但项目在具体实施和落地的过程中,有一些问题和细节,还是有必要记录一下

切换MySQL数据库

开发的时候默认用的SQLite数据库,到了正式环境,需要切换到CS架构的服务器,比如MySQL

我的配置是这样

# 数据库配置
database_config = {
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'mysql': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'name',
'USER': 'root',
'PASSWORD': 'password',
'HOST': 'mysql' if DOCKER else 'localhost',
'PORT': '3306',
}
}
DATABASES = {'default': database_config['sqlite3']}

这样方便切换不同的数据库,其实还可以把数据库切换用环境变量来控制,docker中使用MySQL,本地开发环境使用sqlite。

MySQL的HOST配置是:'mysql' if DOCKER else 'localhost' 也是为了适配本地环境和docker环境的切换,后续我再封装一下 DjangoStarter 的数据库配置部分,实现前面说的环境变量切换配置。

大量数据导入问题

本项目遇到的第一个麻烦的问题是大量的数据导入

客户提供的数据是Excel格式,大概几百万条吧,我首先使用Python对数据进行预处理,做了一些去重、数据清洗之类的操作,然后导出成JSON文件。

然后在导入Django数据库的时候就遇到了问题,DjangoORM的速度太慢了!

让他跑数据跑了一个晚上,才导入了80w条数据左右,这肯定不行啊,因为被业务部门捅了娄子,项目还有几天的时间就要上线了,要赶!总共数据有几百万呢……

没办法,只能直接上SQL了,掏出Navicat,连上服务器的MySQL数据库,然后把数据直接导入临时表,再用SQL一番折腾,导入到Django生成的表里,这样数据的问题就搞定了。

(当然后续还有一系列的数据问题,前期的数据清洗还是不够的,后面发现的一些数据缺失啥的问题,在赶进度的过程中边处理,做了一些补救措施,最后也还好勉强可以用)

下次数据清洗还是得试一下Pandas这种专业的工具,单靠Python本身不够。

接口缓存

由于数据量太大,有几个需要计算操作的接口是比较慢的,该优化的暂时都优化了(下面或许会写一下DjangoORM的优化),所以只能上缓存了

项目用了我的「DjangoStarter」项目模板,本身集成了Redis支持,所以缓存直接用Redis的就好了。

缓存有两种使用方式,用Django默认的cache_page装饰器,或者第三方库rest_framework_extensions

前者作用于 function view 上,当然 class view 也能用,但是得加 method_decorator,然后得自己封装一个缓存过期配置。

后者可以使用 rest_framework 的缓存配置,相对来说更方便,只是需要安装一个库。(另外提一点,rest_framework_extensions这个库还有其他的一些功能,有空再介绍,感兴趣的同学可以探索一下)

最终我选择了第二个,哈哈~

不过我都介绍一下吧,很简单

cache_page 的使用

from django.views.decorators.cache import cache_page
from rest_framework.decorators import api_view @cache_page(CACHE_TIMEOUT)
@api_view()
def overview(request: Request):
...

其中 CACHE_TIMEOUT 的单位是秒,也可以设置成 None,这样缓存就永不过期了。

rest_framework_extensions

rest_framework 的缓存配置

REST_FRAMEWORK = {
# 缓存过期时间
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 60,
}

安装

pip install drf-extensions

使用

常用的两种方式,装饰器和Mixin

装饰器用法

from rest_framework_extensions.cache.decorators import cache_response

class ViewSet(viewsets.ModelViewSet):
... @cache_response()
def list(self, request, *args, **kwargs):
...

Mixin用法,注意 CacheResponseMixin 要放在 ModelViewSet 的前面

from rest_framework_extensions.cache.mixins import CacheResponseMixin

class ViewSet(CacheResponseMixin, viewsets.ModelViewSet):
...

参考资料

响应数据量太大问题

前面那个可视化地图的页面,需要获取几万条人员信息,这个接口一开始没做优化,返回的数据大小有50MB,就很离谱,单纯网络传输就用了40秒,卡的一批。

前端小伙伴反映这个问题后,我查看一下服务器的日志,发现响应时间就达到了5秒,这忍不了啊。一开始是加了缓存,效果显著,响应时间直接压缩到了0.1秒!不过没用,传输时间还是很长。

继续分析,因为是用DjangoStarter的自动代码生成功能实现的接口,所以请求之后会默认返回人员信息的所有字段,但很明显,地图上只需要三个字段:ID、经纬度。

所以我重新写了个 serializer

class BasicPersonSerializer(serializers.ModelSerializer):
class Meta:
model = BasicPerson
fields = ['id', 'address_lng', 'address_lat']

然后在 viewsets 里也重写了 list 方法,用上新定义的这个 serializer

class BasicPersonViewSet(viewsets.ModelViewSet):
... @cache_response()
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset)
if page is not None:
serializer = BasicPersonSerializer(page, many=True)
return self.get_paginated_response(serializer.data) serializer = BasicPersonSerializer(queryset, many=True)
return Response(serializer.data)

然后再来测试一下,响应时间69毫秒,数据量变成4MB+,效果很显著!

整个传输时间只需要1.2秒左右~

妙啊,但是我还不满足,既然还有优化空间,那就继续优化。

给接口加个gzip压缩吧~

为图省事,直接上全站压缩

MIDDLEWARE = [
'django.middleware.gzip.GZipMiddleware',
]

搞定

再次请求看看

数据大小被压缩到480kb,传输时间只需要356毫秒!妙啊

这个问题就搞定了。

参考资料:Django使用gzip实现压缩请求 - https://www.pythonf.cn/read/116970

聚合查询

既然用Django了,有了这么方便好用的ORM,就别老想着用什么SQL语句了。

我在这个项目里比较常用的是这几个

  • aggregate
  • annotate
  • values (虽然这个可能不算,但用的很多)
  • Count
  • Sum

每个函数的具体用法我就不复制粘贴了,看下面的参考资料吧~

参考资料:

性能优化

Django性能确实有点一言难尽,性能优化也老生常谈了,不过就实际运用而已,我们这也还是在探索之中,因为大部分场景是够用的,没有多高的并发。

不过有个比较慢的地方是展示大屏的数据接口,因为要汇集好几个表的数据进行统计,几十万上百万的数据,有点慢,一开始响应时间需要40秒,这也太离谱了。

肯定是得优化的,优化思路从减少数据库访问次数、合并运算、增加缓存入手,优化完成之后冷启动速度5秒,命中缓存60毫秒内,效果还是可以的。

关于性能优化这块以后还是得继续看看,Django有太多可以优化的地方了……

(或者不行的话直接用.Net Core这种高性能的平台重写?)

部署

部署方面依然是 uwsgi / docker / docker-compose 这套组合,之前用了好多次了,比较稳定,配置文件都是现成的,直接把代码上传服务器 up一下就启动了,非常方便。

对了还需要配置一下nginx,uwsgi是专有协议,需要做个转发,才能使用http访问到。

部署之后关闭debug模式,还需要进入docker容器里,在bash里执行 collectstatics 收集静态文件。

之后要更新的话,只需要在pycharm里配置 commit 的时候顺便deploy,把修改的代码文件提交到服务器,然后修改一下 readme.md 文件(我配置了监听这个文件)即可重启服务。

小结

OK,大概就是这样了,项目也不是到这里就结束了,只是暂告一个段落,接下来看看客户那边有什么新的需求再来继续开发。

Django框架陆陆续续也用了两三年的时间了,虽然应用场景都比较简单,但属于是基本摸清了开发流程和定制的上限,像django-admin这种内置的管理后台,尽管有大量的自定义配置功能,还有simpleUI这种优秀的第三方界面,但他的上限还是摆在那,遇到稍微需要定制化的管理后台需求,还是得自己搞一套,好在用RestFramework写接口实在是方便,接口导出来,套个vue+elementUI的前端,一套后台就搞定了。

如果还需要数据大屏这类可视化功能,我们现在也积累了一些这方面的技术和经验,可以比较快的出产品。

接下来很多新的项目还是优先使用.Net Core技术,从稳定性和性能的方面考虑,我还是更信赖.Net Core。

Django的优势主要还是在于开发效率和背靠Python社区的强大生态,不过性能和部署方便就要稍逊一点点。

总之,都好用,看场景使用~

项目完成 - 基于Django3.x版本 - 开发部署小结的更多相关文章

  1. 项目完成小结 - Django3.x版本 - 开发部署小结 (2)

    前言 好久没更新博客了,最近依然是在做之前博客说的这个项目:项目完成 - 基于Django3.x版本 - 开发部署小结 这项目因为前期工作出了问题,需求没确定好,导致了现在要做很多麻烦的工作,搞得大家 ...

  2. NutzWk 5.0.x 微服务分布式版本开发及部署说明

    NutzWk 5.x 已发布一段时间,这段时间基于此版本开发了智慧水务系统(NB-IOT).某物联网平台.某设备租赁平台.某智慧睡眠平台.某智慧园区项目等,开发和部署过程中遇到一些小问题,开这个帖子把 ...

  3. HubbleDotNet 最新绿色版,服务端免安装,基于eaglet 最后V1.2.8.9版本开发,bug修正,支持一键生成同步表

    HubbleDotNet 是一个基于.net framework 的开源免费的全文搜索数据库组件.开源协议是 Apache 2.0.HubbleDotNet提供了基于SQL的全文检索接口,使用者只需会 ...

  4. VisualStudio2013 如何打开之前版本开发的(.vdproj )安装项目

    当你的项目使用早于 visualstudio2013 的版本开发并且使用 Visual Studio Installer 制作安装项目时,在升级至 VS2013 后会发现新安装项目无法打开, VS20 ...

  5. 使用ASP.NET MVC、Rabbit WeixinSDK和Azure快速开发部署微信后台

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:公众号后台系统和数据都基本准备妥当了,可以来分享下我是如何开发本微信公众号的后台系统了 ...

  6. RDIFramework.NET V2.8版本 ━ 开发实例之产品管理(WinForm)

    RDIFramework.NET V2.8版本 ━ 开发实例之产品管理(WinForm) 现在,我们使用.NET快速开发整合框架(RDIFramework.NET)来开发一个应用,此应用皆在说明如何使 ...

  7. IbatisNet开发使用小结

    一.   介绍 平常做企业级应用,需求变化是经常的事,而很多基础代码重复也是很让人头疼的问题.所以很多人会使用一些ORM框架来增强项目的可维护性.可扩展性.IBatis.Net就是一个比较易用的ORM ...

  8. 在微服务系统开发部署中使用Azure RBAC自定义角色

    Azure的官方文档介绍了如何创建用于Azure基于角色的访问控制的自定义角色(RBAC Role). 我们也可以根据同样的原理把RBAC细粒度资源管理运用于微服务产品的开发部署中.(https:// ...

  9. git开发部署流程

    git的分支操作 https://blog.csdn.net/QH_JAVA/article/details/77853605 Git 开发部署流程 采用业界成熟方案 Git Flow 分支方式进行开 ...

随机推荐

  1. Apache+tomcat实现应用服务器集群

    Ngnix/Apache比较 Nginx:Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行.其特点是占有内存少,并发能力 ...

  2. IO笔记(学习尚硅谷java基础教程)

    一.基础知识 1. 在普通方法和测试方法中文件路径的差异 在普通方法中:文件路径相当于在当前项目中,而不是当前Module(以项目为基准) 在测试方法中:文件路径相当于在当前Module中,而不是当前 ...

  3. Linux(centos7)安装RabbitMQ

    由于RabbitMQ是由Erlang语言开发的,所以我们需要体检安装erlang语言的环境 下载这三个安装包:https://www.aliyundrive.com/s/4AxfTepHjMD 执行安 ...

  4. park和unpark

    1 介绍 LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语.LockSupport提供的两个主要方法就是park和unpark. park译为&quo ...

  5. 解释 Spring 框架中 bean 的生命周期?

    Spring 容器 从 XML 文件中读取 bean 的定义,并实例化 bean. Spring 根据 bean 的定义填充所有的属性. 如果 bean 实现了 BeanNameAware 接口,Sp ...

  6. 学习heartbeat-02安装及配置

    2.部署Heartbeat高可用需求 2.1 操作系统 CentOS-6.8-x86_64 2.2 Heartbeat服务主机资源准备 主服务器A: 主机名:heartbeat-1-130 eth0网 ...

  7. Java设置方法模板

  8. 51单片机头文件reg51.h详解

    转自:http://www.51hei.com/mcu/2670.html 我们在用c语言编程时往往第一行就是头文件,51单片机为reg51.h或reg52.h,51单片机相对来说比较简单,头文件里面 ...

  9. 小程序canvas文本换行生成图片

    一.图片透明及旋转 let ctx = wx.createCanvasContext('shareImg') ctx.drawImage('../../../' + res[0].path, 0, 0 ...

  10. 在create-react-app使用less与antd按需加载

    使用antd按需加载 使用react-app-rewired对 create-react-app 的默认配置进行自定义 yarn add react-app-rewired --dev /* pack ...