此处衔接我在个人wiki上面写的三篇Django快速开发的博客
一、Django集成Sentry
Sentry是一款开源的日志聚合系统,它支持很多平台,也可以监控Django运行产生的日志,其中最需要关注的就是自动上报未捕获的错误日志!使用Sentry服务有两种方式:
- Sentry的Docker官方服务,量大需要付费,优点是使用方便。
- 自建服务:使用Docker来部署Sentry,优点是免费,生产环境可以考虑使用官方收费服务。
1.1、Docker环境配置
已安装请跳过并直接开始1.2小节。
1.1.1、Docker安装
本次示例为ArchLinux系列的Manjaro21系统,其它系统请自行更换安装命令。
sudo pacman -S docker docker-compose # 安装docker
sudo mkdir /etc/docker
sudo vim /etc/docker/daemon.json
1.1.2、配置加速服务
加入以下配置到/etc/docker/daemon.json
,感谢中国科技大学提供的Docker镜像源免费加速服务,大家也可以使用国内其它加速服务。
{
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/"]
}
1.1.3、配置Docker服务
sudo systemctl daemon-reload # 重载系统服务配置
sudo systemctl enable docker
sudo systemctl restart docker
sudo usermod -a sanxi -G docker # 将sanxi加入docker组,即可无须sudo也能执行docker命令,重新登陆才生效。
1.2、Sentry部署
本文以部署Sentry 21.4.1版本为例,先去GitHub下载Sentry的on-premise版本,这也是官方推荐的部署方式!安装Sentry前请先确保本地docker服务正在运行及docker-compose已安装!
1.2.1、环境准备
wget https://github.com/getsentry/onpremise/archive/refs/tags/21.4.1.tar.gz # 下载
tar -xvf 21.4.1.tar.gz # 解压
cd onpremise-21.4.1/
bash install.sh # 开始自动部署,耗时较长,请耐心等待
1.2.2、启动sentry服务
注意:必须保证在onpremise目录下,有docker-compose.yml
文件,如果前面运行脚本失败这里是不会有此文件的。
docker-compose up -d # -d,后台运行
1.3、Django集成sentry
1.3.1、登陆sentry
默认的sentry端口是9000,访问对应地址后,界面说实话还挺好看的。
添加sentry用户
如果在一开始执行install.sh时忘记了添加sentry用户,可以使用以下格式命令进行用户的添加:
[sanxi@sanxi-aero15sa onpremise-21.4.1]$ docker-compose run --rm web createuser --help
Creating sentry_onpremise_web_run ... done
Usage: sentry createuser [OPTIONS]
Create a new user.
Options:
--email TEXT
--password TEXT
--superuser / --no-superuser
--no-password
--no-input
--force-update
--help
# 按照上面的选项添加用户
[sanxi@sanxi-aero15sa onpremise-21.4.1]$ docker-compose run --rm web createuser --email admin@qq.com --password admin --superuser
Creating sentry_onpremise_web_run ... done
08:17:09 [INFO] sentry.plugins.github: apps-not-configured
Added to organization: sentry
User created: admin@qq.com
第一次登陆成功后,会弹出一个配置后台窗口,我们是本地环境,直接默认即可。
1.3.2、创建project
图片已经弄丢了,反正都是图形界面,点两下就知道了。
1.3.3、settings配置
创建完project后,sentry会给出Configure Django
信息,按照信息所说的来配置Django即可。
首先,是安装sentry的SDK。
pip install --upgrade sentry-sdk
接着,在Django项目的settings里面配置,一定要记得必须是Configure Django
给的代码才行,因为里面含有你刚才创建的项目的密钥,这是最关键的信息,你也可以自己在sentry的setting里面查看每个project的密钥信息。
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn="http://44b110ca269a468cbc54d97da336a35a@127.0.0.1:9000/3",
integrations=[DjangoIntegration()],
# Set traces_sample_rate to 1.0 to capture 100%
# of transactions for performance monitoring.
# We recommend adjusting this value in production.
traces_sample_rate=1.0, # 这个是日志采集率,1.0就是100%,生产环境要改,不然影响性能。
# If you wish to associate users to errors (assuming you are using
# django.contrib.auth) you may enable sending PII data.
send_default_pii=True
)
1.3.4、测试sentry
配置完sentry后,可以在urls.py中添加以下代码进行测试sentry是否可用!然后访问sentry-debug,比如我服务是在本地,所以是访问:http://127.0.0.1:8000/sentry-debug/
,因为Python中1不能除以0,所以一定会报错,这时候回到sentry就可以看到错误日志了。
from django.urls import path
def trigger_error(request):
division_by_zero = 1 / 0
urlpatterns = [
path('sentry-debug/', trigger_error),
# ...
]
中间件捕获异常上报到钉钉
使用Django自定义中间件的process_exception
二、集成Rest Framework
Django Rest Framework,简称drf,基于Django框架进行二次开发,是对Rest API的一种实现!基础部分可以看我这篇博客:《RESTful与django》。
2.1、安装drf包
如果不是默认安装到当前用户家目录,则需要使用--user
选项指定安装包到本地,这样就不存在无权限访问的错误。
pip install djangorestframework
2.2、创建序列化器
为项目创建序列化器,建议单独模块的方式存放于utils目录。
from rest_framework import serializers
from interview.models import Candidate # 导入表
class InterviewSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Candidate # 将要序列化的表
fields = ('name', 'gender', 'degree') # 展示的字段
2.3、视图类
drf视图层的基础可以看我这篇博客:《Rest Framework视图层》:https://blog.sanxi.info/archives/6-6--r-e-s-t--f-r-a-m-e-w-o-r-k-shi-tu-lei, viewset是更进一步的封装。
from django.contrib.auth.models import User
from rest_framework import viewsets
from rest_framework import permissions
from utils.serializers import InterviewSerializer
# Create your views here.
class InterviewViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Candidate.objects.all()
serializer_class = InterviewSerializer
permission_classes = [permissions.IsAuthenticated]
2.4、路由配置
在urls.py
中配置参考如下配置:
from rest_framework import routersfrom interview.views import InterviewViewSetrouter = routers.DefaultRouter()router.register(r'interview', InterviewViewSet)urlpatterns = [ path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), path('api/', include(router.urls)),]
2.5、注册drf
需要在settings里面将drf注册到APP中
INSTALLED_APPS = [ ... 'rest_framework',]
2.6、分页器
分页器可以看看我这篇博客:Django Rest Framework组件
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 # 每页展示条数}
2.7、验证
这时候我们再去访问127.0.0.1:8000/api/interview/
,就可以看到返回相应的数据了。
{
"count": 5,
"next": null,
"previous": null,
"results": [
{
"name": "张三",
"gender": "男",
"degree": "本科"
},
{
"name": "刘倩",
"gender": "女",
"degree": "本科"
},
{
"name": "陈欣欣",
"gender": "女",
"degree": "硕士"
},
{
"name": "刘德华",
"gender": "男",
"degree": "硕士"
},
{
"name": "李小龙",
"gender": "男",
"degree": "本科"
}
]
}
三、Django集成redis
django-redis 基于 BSD 许可, 是一个使 Django 支持 Redis cache/session 后端的全功能组件。更多详情请参阅
django-redis
官方文档:https://django-redis-chs.readthedocs.io/zh_CN/latest/
3.1、安装
pip install django-redis # 安装redis集成包
sudo pacman -S redis
systemctl start redis
3.2、作为缓存的配置
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "TIMEOUT": 300, "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "SOCKET_CONNECT_TIMEOUT": 5, # 创建redis连接超时时间 "SOCKET_TIMEOUT": 5, # 读写超时时间 } }}CACHE_MIDDLEWARE_SECONDS = 600
3.3、整站缓存
在common中间件前后各加一个中间件,启用后访问时看页面的response headers中的cache-control就可以判定是否缓存成功,也可以加个日志做。
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.cache.UpdateCacheMiddleware', # common前面 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', # common后面 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',]
3.4、验证
当我们访问后台时,页面返回的response headers
如下所示:
HTTP/1.1 200 OK
Date: Fri, 14 May 2021 06:22:58 GMT
Server: WSGIServer/0.2 CPython/3.9.4
Content-Type: text/html; charset=utf-8
Vary: Accept, Cookie, Accept-Language
Allow: GET, POST, HEAD, OPTIONS
X-Frame-Options: SAMEORIGIN
Content-Length: 10428
Expires: Fri, 14 May 2021 06:30:15 GMT
Cache-Control: max-age=600
Content-Language: zh-hans
四、Django集成celery
参考celery官方文档:https://docs.celeryproject.org/en/stable/django/first-steps-with-django.html
celery是一种基于Python开发的分布式消息队列,在**celery4.0+**开始集成Django了,它可以很方便地在Django中执行异步任务。
pip install celery
4.1、celery异步任务
本次目的为学习如何在Django项目中使用celery来执行异步任务
4.1.1、目录结构
[sanxi@sanxi-aero15sa ~]$ tree recruitment2/ -I static
recruitment2/
├── db.sqlite3
├── interview
├── manage.py
├── recruitment # 项目根目录,也是默认的settings.py所在目录,我这里因为删了,所以没有。
│ ├── celery.py # celery必须放在项目根目录
│ ├── __init__.py # 在此处导入celery的app
│ ├── urls.py
│ └── wsgi.py
└── utils
4.1.2、celery异步执行任务
在项目根目录下创建名为celery.py
的文件,代码如下所示:
import osfrom celery import Celery# set the default Django settings module for the 'celery' program.os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.base_settings')app = Celery('recruitment')# Using a string here means the worker doesn't have to serialize# the configuration object to child processes.# - namespace='CELERY' means all celery-related configuration keys# should have a `CELERY_` prefix.# namespace固定了settings配置格式必须是CELERY开头app.config_from_object('django.conf:settings', namespace='CELERY')# Load task modules from all registered Django app configs.app.autodiscover_tasks()@app.task(bind=True)def debug_task(self): print(f'Request: {self.request!r}')
4.1.3、导入app
在celery.py
同目录下的__init__.py
中导入上面创建的celery模块中celery对象:app
# This will make sure the app is always imported when# Django starts so that shared_task will use this app.from .celery import app as celery_app__all__ = ('celery_app',)
4.1.4、settings配置
backend是异步任务运行结果存储位置,broker是存储任务的系统的代理,也是一个消息队列。
# Celery Configuration Options
CELERY_TIMEZONE = "Asia/Shanghai"
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60
CELERY_BROKER_URL = "redis://localhost:6379/0"
CELERY_RESULT_BACKEND = "redis://localhost:6379/1"
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_RESULT_SERIALIZER = 'json'
CELERYD_MAX_TASKS_PER_CHILD = 10
CELERYD_LOG_FILE = os.path.join(BASE_DIR, "logs", "celery_worker.log")
CELERYBEAT_LOG_FILE = os.path.join(BASE_DIR, "logs", "celery_beat.log")
4.1.5、创建tasks
在Django的app下面创建tasks.py
,在这里编写需要执行的异步任务。
# Create your tasks here
from celery import shared_task
from dingtalkchatbot.chatbot import DingtalkChatbot # 导入钉钉官方SDK
from settings.base_settings import WEBHOOK, SECRET # 自定义的配置项,可以放在任何地方。
@shared_task # 共享任务,用以异步执行。
def send_dingtalk_message(message):
xiaoding = DingtalkChatbot(WEBHOOK, secret=SECRET, pc_slide=True)
xiaoding.send_text(msg=message, is_at_all=True)
4.1.6、修改代码为异步执行
再次提醒,异步任务不要滥用,只应该在产生I/O操作的地方使用异步任务,我们的项目中发送消息到钉钉会产生网络I/O,可以在这里改一改原来的代码,将它从同步执行改为celery异步执行。
from .tasks import send_dingtalk_message # 导入tasks
def first_interview(modelsadmin, request, queryset):
for field in queryset:
seeker_list.append(field.name)
interviewer_list.append(field.first_interviewer_user.username)
send_dingtalk_message.delay(f'候选人 {seeker_list} 已到达,请第一轮面试官 {interviewer_list} 做好准备!')
4.1.7、启动Redis
sudo systemctl atart redis
4.1.8、启动celery
celery命令用法:celery [OPTIONS] COMMAND [ARGS]...
- OPTIONS:-A或--app,指定Django的app,也就是默认
settings.py
所在目录
注意,如果Django不是用默认的settings.py,需要使用选项指定setting文件DJANGO_SETTINGS_MODULE=your settings
!
[sanxi@sanxi-aero15sa recruitment2]$ celery -A recruitment worker -l info
-------------- celery@sanxi-aero15sa v5.0.5 (singularity)
--- ***** -----
-- ******* ---- Linux-5.10.34-1-MANJARO-x86_64-with-glibc2.33 2021-05-16 22:14:07
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: recruitment:0x7f6dfc8ba7f0
- ** ---------- .> transport: redis://localhost:6379/0
- ** ---------- .> results: redis://localhost:6379/1
- *** --- * --- .> concurrency: 12 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. interview.tasks.send_dingtalk_message
. recruitment.celery.debug_task
[2021-05-16 22:14:07,546: INFO/MainProcess] Connected to redis://localhost:6379/0
[2021-05-16 22:14:07,551: INFO/MainProcess] mingle: searching for neighbors
[2021-05-16 22:14:08,563: INFO/MainProcess] mingle: all alone
[2021-05-16 22:14:08,569: WARNING/MainProcess] /home/sanxi/.local/lib/python3.9/site-packages/celery/fixups/django.py:203: UserWarning: Using settings.DEBUG leads to a memory
leak, never use this setting in production environments!
warnings.warn('''Using settings.DEBUG leads to a memory
[2021-05-16 22:14:08,569: INFO/MainProcess] celery@sanxi-aero15sa ready.
4.1.10、flower监控
pip install flower
celery flower --broker=redis://@localhost:6379/0 # 接着启动flower服务,默认5555端口。
4.1.11、验证
此时可以去后台系统,点击通知面试官按钮即可在钉钉群收到对应消息。
4.2、celery定时任务
4.2.1、定时任务简述
celery定时任务由三部分组成:心跳管理、调度器、存储
- beat:调度进程,用于管理和调度定时任务。
- scheduler:调度器,查找任务并检测任务的执行状态,如到点执行,则scheduler执行此任务。
- storage:存储任务的执行状态,有两种方式
- 文件形式存储,也是默认的方式;Default Persistenstorage。
- 数据库存储,将任务执行状态信息存储到数据库中。
图片来源于吕召刚老师
4.2.2、环境准备
安装beat包
pip install django-celery-beat
注册
INSTALLED_APPS = [
'django_celery_beat'
]
数据库迁移
python manage.py makemigrations
python manage.py migrate
4.2.3、Periodic Tasks
此时再打开后台管理界面,会发现多了一个Periodic Tasks界面,此处即为celery的周期任务管理界面,它有以下模块:
- Intervals,用于管理任务运行的周期。
- Crontab,用crontab的格式来定期执行任务。
- Periodic tasks,具体要执行的任务列表。
- Solar events,根据当地日出日落时间来执行任务,这个跟智能家具可以结合使用,比如日落时自动打开灯之前的。
- Clocked,跟Intervals有点类似,在执行时间执行任务。
4.2.4、添加定时任务
其实最方便的是在web界面添加,比代码方便多了。
管理celery定时任务有几种方式:
- 启动时自动注册定时任务
- 直接添加定时任务
- 动态添加定时任务
自动注册
调用DJango的信号添加定时任务,这是代码运行时自动注册定时任务
from celery.schedules import crontab
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
# Calls test('hello') every 10 seconds.
sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')
# Calls test('world') every 30 seconds
sender.add_periodic_task(30.0, test.s('world'), expires=10)
# Executes every Monday morning at 7:30 a.m.
sender.add_periodic_task(
crontab(hour=7, minute=30, day_of_week=1),
test.s('Happy Mondays!'),
)
@app.task
def test(args):
print(args)