django处理多线程访问和操作数据库的安全问题

当多线程对数据库进行访问和操作时,需要考虑到一些并发的问题。django针对此类问题给出了很多解决方案。在django体系中,将每一次的数据库访问称为一次transaction。于此同时,django对数据库的操作还提供了<model_name>.select_for_update().<function>的api。这个select_for_update能够调用数据库的api,对某些数据进行锁定,让并发的其他线程无法立刻访问。

总体上来说,做到多线程安全操作的关键在于2个点,第一,锁定django的transaction防止django内部的多线程互相干涉。第二,锁定数据库的访问,防止多个服务器对数据库的操作互相干涉。

具体操作方法如下:

为Model增加类函数。之所以是类函数,是因为成员函数的本身能够访问时,就已经从数据库提取过数据了。

from django.db import models, transaction

#... in model class...
    @classmethod
    def getPendingTask(cls):
        with transaction.atomic():                                                                      
            try:
                pendings = cls.objects.select_for_update().filter(status = TaskStatus.PENDING)          
                if len(pendings) < 1:                                                                   
                    return False
                first = pendings[0]
                first.convert_start_date = timezone.now()                                               
                first.status = TaskStatus.CONVERTING                                                    
                first.save()                                                                            
                return {
                    'name': first.name,
                    'filename': first.filename                                                          
                }
            except Exception as e:                                                                      
                return False     

docker发布django测试服务器端口至host

django测试服务器运行端口为127.0.0.1:8000。在docker中运行时如果采用--publish 8000:8000的映射方法是会出错的。因为这里的映射ip为0.0.0.0。即8000 -> 0.0.0.0:8000。而django运行的ip为127.0.0.1:8000。端口是同一端口,ip却不是同一个ip。理论上解决此问题的方法有二,其一是修改容器的映射ip,但docker文档不支持修改。例如8000:127.0.0.1:8000。但是docker文档不支持这类操作。于是只能采用第二种方法,改变django运行的ip。例如python manage.py runserver 0.0.0.0:8000

创建django和mysql应用的docker工程

以下列出按照常规操作时会踩的坑

  1. django2.2.x版本目前暂时不支持mysql8.x版本。(20191117)
  2. django的默认数据库为SQLite,要改为mysql需要修改<project>/<project>/sittings.py中的database数值。例如:
    DATABASE = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'django_test', #此处为数据库名称
    'USER': 'username', 该对数据库拥有访问权限的用户名称
    'PASSWORD': 'password',
    'HOST': 'mysql-test', 这里的值是该数据库的域名名称
    'PORT': '3306'
    }
    }
  3. mysql的docker运行指令示例:
    docker run --network="django_test" --rm -d -e MYSQL_ROOT_PASSWORD=my-secret -e MYSQL_DATABASE=django_test -e MYSQL_USER=username -e MYSQL_PASSWORD=password --publish 3307:3306 --name mysql-test --hostname mysql-test mysql:5.7