django基础性能优化

django性能优化

方式1:压缩django响应体

  • 通过压缩响应json数据,从而加快响应速度,并且django-compression-middleware支持多种浏览器(除了IE11)。

  • 下载django-compression-middleware

  • 在settings配置:

    MIDDLEWARE = [
      ...
        "django.middleware.common.CommonMiddleware",
        "django.contrib.auth.middleware.AuthenticationMiddleware",
        "compression_middleware.middleware.CompressionMiddleware",
      ...
    ]
  • 使用django-compression-middleware之前:

    django基础性能优化

  • 使用django-compression-middleware之后:

    django基础性能优化

  • 可以看到,压缩后后响应体,Content-length相比原来缩小了六分之一。需要知道的是Django文档GZipMiddleware章节所说,压缩可能会导致网站出现安全漏洞。

方式2:通过分页来

  • 前置条件,为了看得更加明显我们用django-debug-toolbar,一个调试的组件,配置详见Django:基于调试组插件go-debug-toolbar。举个简单小例子,比方出版社和书是一对多关系
  • 普通查询
class Bookser(serializers.Serializer):
    """序列化器"""
    id = serializers.IntegerField(read_only=True)
    pub_name = serializers.CharField(source="publisher.name", read_only=True)
    title = serializers.CharField(max_length=32)
    put_time = serializers.DateField()
    
    
class Book(APIView):
    """视图函数"""
    def get(self,request):
        books = models.Book.objects.all()
        ser_obj = Bookser(instance = books,many=True)
        return Response(ser_obj.data)
# 这里做的是查询书信息和出版社。
  • 这里看到,做了13次查询获得结果:

    django基础性能优化

  • 通过select_related查询:

class Book(APIView):
    def get(self,request):
        books = models.Book.objects.select_related("publisher")
        res = []
        for book in books:
            res.append({"id":book.id,"pub_name":book.publisher.name,"title":book.title,"put_time":book.put_time})
        ser_obj = Bookser(instance=res, many=True)
        return Response(ser_obj.data)
  • 通过select_related查询次数就一次:

    django基础性能优化

  • 获取每本书和其对应作者
  • 普通查询:
class Bookser(serializers.Serializer):
    """序列化多对多"""
    id = serializers.IntegerField(read_only=True)
    pub_name = serializers.CharField(source="publisher.name", read_only=True)
    title = serializers.CharField(max_length=32)
    put_time = serializers.DateField()
    author = serializers.SerializerMethodField(read_only=True)
    # 获取每个书对象对应作者
    def get_author(self,obj):
        author = obj.authors.all()
        auth = AuthorSerializer(author, many=True)
        return auth.data
        
class Book(APIView):
    """视图函数"""
    def get(self,request):
        books = models.Book.objects.all()
        # 序列化 books对象,获得书和作者数据
        ser_obj = Bookser(instance = books,many=True)
        return Response(ser_obj.data)
  • 这里看到,做了25次查询获得结果:

    django基础性能优化

  • 通过prefetch_related查询:

class Book(APIView):
    def get(self,request):
        res = []
        books = models.Book.objects.prefetch_related("authors")
        for book in books:
            res.append(
                {
                    "id":book.id,
                    "title": book.title,
                    "put_time": book.put_time,
                    "author": [{"author_name":author.author_name,"id":author.id} for author in book.authors.all()]
                }
            )
        return Response(res)
  • 通过prefetch_related查询次数就2次:

    django基础性能优化

  • 小结:

    通过select_related和prefetch_related需要注意:
      如果不建立数据库连接池,效果会更明显,因为减少了数据库往返次数
      若果结果集过大,使用prefetch_related反而会使速度变慢
      对一个数据库查询不一定比两个或多个快。

方式5:查询需要的内容

  • 使用values或values_list来获取所需数据库对象属性。

方式6:异步执行通过Celery

  • django中使用celery进行异步任务。异步任务分为任务发出者-->任务队列--->任务处理者。任务中间人可以选择rabbitMQ或redis.这里使用redis实现。整个流程如下图:

django基础性能优化

  • 下载:

    pip install celery
    pip install django-celery
    pip install redis
  • 文件目录:

    djangoSerializers
      |__ api
          |___ __init__.py 
          |___ taksk.py
          |___ views.py
          |___ ...
      |__ djangoSerializers
          |___ __init__.py
          |___ settings.py
             |___ urls.py
             |___ wsgi.py
             |___ celery.py
  • 在settings配置:

    # 1.建立celery
    INSTALLED_APPS = [
      'djcelery'
    ]
    # 2. 基本配置
    
    import djcelery
    djcelery.setup_loader() # 加载djcelery
    # 数据库调度
    BROKER_TRANSPORT='redis' #指定redis
    CELERYBEAT_SCHEDULER='djcelery.schedulers.DatabaseScheduler' # celey处理器,固定
    CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0' # Broker配置,使用Redis作为消息中间件
    CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1' # BACKEND配置,这里使用redis
    CELERY_IMPORTS = ('api.tasks')
    # CELERY_RESULT_SERIALIZER = 'json' # 结果序列化方案
    #允许的内容类型,
    CELERY_ACCEPT_CONTENT=['pickle','json']
    #任务的序列化方式
    CELERY_TASK_SERIALIZER = 'json'
    #celery时区
    CELERY_TIMEZONE = 'Asia/Shanghai'
    
  • 在同级的__init__.py文件配置

    from __future__ import absolute_import
    from .celery import app as celery_app
    # 这是为了确保在django启动时启动 celery
  • tasks.py定义worker函数。

    from __future__ import absolute_import
    from djangoSerializers.celery import app
    import time
    @app.task
    def add(x,y):
        """任务处理"""
        time.sleep(30)
        return x + y
  • 视图中使用任务处理函数:

    from djangoSerializers.celery import app
    from rest_framework.response import Response
    
    class CeleryView(APIView):
        def get(self,request):
            #执行异步任务
            result = add.delay(2,5)
            # 获取任务id
            task_id = result.id
            # 获取任务状态
            status = app.AsyncResult(task_id).status
            return Response({"code":200,"msg":"OK","status":status,"task_id":task_id})
  • 任务状态有:

    'PENDING': '等待开始',
    'STARTED': '任务开始',
    'SUCCESS': '成功',
    'FAILURE': '失败',
    'RETRY': '重试',
    'REVOKED': '任务取消',
  • 在终端执行:

    python manage.py help
    • 可以看到djcelery执行命令:

    django基础性能优化

    • 通过执行如下命令,启动worker节点:

      python3 manage.py celery worker -l INFO
  • 查看执行状态:

    class SearchCeleryView(APIView):
        def get(self,request):
            # 获取任务id
            task_id = request.query_params.get('taskid')
            # 查看任务状态
            status = app.AsyncResult(task_id).status
            # 查看任务执行结果,没有执行完是None
            result = app.AsyncResult(task_id).result
            print(status,result)
            return Response({"status":status,"result":result})
    • 注意:
    # 在settings我们之前配置了允许数据类型pickle,否则会报错:kombu.exceptions.ContentDisallowed: Refusing to deserialize untrusted content of type pickle (application/x-python-serialize)
  • 定时任务:

    • settings.py配置
    # 定义队列名字
    CELERY_QUEUES = {
        'beat_tasks': {
            'exchange': 'beat_tasks',
            'exchange_type': 'direct',
            'binding_key': 'beat_tasks'
        },
        'work_queue': {
            'exchange': 'work_queue',
            'exchange_type': 'direct',
            'binding_key': 'work_queue'
        }
    }
    # 默认执行队列
    CELERY_DEFAULT_QUEUE = 'work_queue'
    • 其他配置
    #  有些情况下可以防止死锁
    CELERYD_FORCE_EXECV = True
    
    #  设置并发的worker数量
    CELERYD_CONCURRENCY = 4
    
    # 允许重试
    CELERY_ACKS_LATE = True
    
    #  每个worker最多执行100个任务被销毁,可以防止内存泄漏
    CELERYD_MAX_TASKS_PER_CHILD = 100
    
    #  单个任务的最大运行时间,超过就杀死
    CELERYD_TASK_TIME_LEMIT = 12 * 30
    • 创建定时任务:
    CELERYBEAT_SCHEDULE = {
        'helloworld':{
          # 指向处理任务的函数
            "task": "api.tasks.hellworld",
            # 执行时间 5秒一次
            "schedule": timedelta(seconds=5),
            # args传参
            "args":{},
            # 指定任务队列 在beat_tasks队列上的
            "options": {
                "queue": 'beat_tasks'
            }
        }
    }
    • 在tasks.py定义简单定时任务
    @app.task
    def hellworld():
        time.sleep(3)
        print("hello world is run...")
        return "hello world"
    • 终端执行:
    # 启动worker来处理任务
    python3 manage.py celery worker -l INFO
    # 启动定时任务,当到时间时,把任务放入broker中,broker检测到了,让worker去工作。
    python manage.py celery beat -l info 
上一篇:celery基础


下一篇:快速创建Flask Restful API项目