项目完成 - 基于Django3.x版本 - 开发部署小结
前言
最近因为政企部门的工作失误,导致我们的项目差点挂掉,客户意见很大,然后我们只能被动进入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):
...
参考资料
- drf实现常用数据缓存:https://segmentfault.com/a/1190000018170069
响应数据量太大问题
前面那个可视化地图的页面,需要获取几万条人员信息,这个接口一开始没做优化,返回的数据大小有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 文档 | Django (djangoproject.com)
- 075: 【Django数据库】ORM聚合函数详解-Sum - zheng-weimin - 博客园 (cnblogs.com)
性能优化
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版本 - 开发部署小结的更多相关文章
- 项目完成小结 - Django3.x版本 - 开发部署小结 (2)
前言 好久没更新博客了,最近依然是在做之前博客说的这个项目:项目完成 - 基于Django3.x版本 - 开发部署小结 这项目因为前期工作出了问题,需求没确定好,导致了现在要做很多麻烦的工作,搞得大家 ...
- NutzWk 5.0.x 微服务分布式版本开发及部署说明
NutzWk 5.x 已发布一段时间,这段时间基于此版本开发了智慧水务系统(NB-IOT).某物联网平台.某设备租赁平台.某智慧睡眠平台.某智慧园区项目等,开发和部署过程中遇到一些小问题,开这个帖子把 ...
- HubbleDotNet 最新绿色版,服务端免安装,基于eaglet 最后V1.2.8.9版本开发,bug修正,支持一键生成同步表
HubbleDotNet 是一个基于.net framework 的开源免费的全文搜索数据库组件.开源协议是 Apache 2.0.HubbleDotNet提供了基于SQL的全文检索接口,使用者只需会 ...
- VisualStudio2013 如何打开之前版本开发的(.vdproj )安装项目
当你的项目使用早于 visualstudio2013 的版本开发并且使用 Visual Studio Installer 制作安装项目时,在升级至 VS2013 后会发现新安装项目无法打开, VS20 ...
- 使用ASP.NET MVC、Rabbit WeixinSDK和Azure快速开发部署微信后台
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:公众号后台系统和数据都基本准备妥当了,可以来分享下我是如何开发本微信公众号的后台系统了 ...
- RDIFramework.NET V2.8版本 ━ 开发实例之产品管理(WinForm)
RDIFramework.NET V2.8版本 ━ 开发实例之产品管理(WinForm) 现在,我们使用.NET快速开发整合框架(RDIFramework.NET)来开发一个应用,此应用皆在说明如何使 ...
- IbatisNet开发使用小结
一. 介绍 平常做企业级应用,需求变化是经常的事,而很多基础代码重复也是很让人头疼的问题.所以很多人会使用一些ORM框架来增强项目的可维护性.可扩展性.IBatis.Net就是一个比较易用的ORM ...
- 在微服务系统开发部署中使用Azure RBAC自定义角色
Azure的官方文档介绍了如何创建用于Azure基于角色的访问控制的自定义角色(RBAC Role). 我们也可以根据同样的原理把RBAC细粒度资源管理运用于微服务产品的开发部署中.(https:// ...
- git开发部署流程
git的分支操作 https://blog.csdn.net/QH_JAVA/article/details/77853605 Git 开发部署流程 采用业界成熟方案 Git Flow 分支方式进行开 ...
随机推荐
- Apache+tomcat实现应用服务器集群
Ngnix/Apache比较 Nginx:Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行.其特点是占有内存少,并发能力 ...
- IO笔记(学习尚硅谷java基础教程)
一.基础知识 1. 在普通方法和测试方法中文件路径的差异 在普通方法中:文件路径相当于在当前项目中,而不是当前Module(以项目为基准) 在测试方法中:文件路径相当于在当前Module中,而不是当前 ...
- Linux(centos7)安装RabbitMQ
由于RabbitMQ是由Erlang语言开发的,所以我们需要体检安装erlang语言的环境 下载这三个安装包:https://www.aliyundrive.com/s/4AxfTepHjMD 执行安 ...
- park和unpark
1 介绍 LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语.LockSupport提供的两个主要方法就是park和unpark. park译为&quo ...
- 解释 Spring 框架中 bean 的生命周期?
Spring 容器 从 XML 文件中读取 bean 的定义,并实例化 bean. Spring 根据 bean 的定义填充所有的属性. 如果 bean 实现了 BeanNameAware 接口,Sp ...
- 学习heartbeat-02安装及配置
2.部署Heartbeat高可用需求 2.1 操作系统 CentOS-6.8-x86_64 2.2 Heartbeat服务主机资源准备 主服务器A: 主机名:heartbeat-1-130 eth0网 ...
- Java设置方法模板
- 51单片机头文件reg51.h详解
转自:http://www.51hei.com/mcu/2670.html 我们在用c语言编程时往往第一行就是头文件,51单片机为reg51.h或reg52.h,51单片机相对来说比较简单,头文件里面 ...
- 小程序canvas文本换行生成图片
一.图片透明及旋转 let ctx = wx.createCanvasContext('shareImg') ctx.drawImage('../../../' + res[0].path, 0, 0 ...
- 在create-react-app使用less与antd按需加载
使用antd按需加载 使用react-app-rewired对 create-react-app 的默认配置进行自定义 yarn add react-app-rewired --dev /* pack ...