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     

调试常用mysql指令

  1. 连接远程mysql库. mysql -u <username> -p -h <remote_host> -P <remote_port>;
    参数解释, -u 指用户名, -p 指连接时使用密码, -h 值的是host, -P后面跟的是端口。
  2. 查看所有数据库. show databases;
  3. 使用某数据库. use <database_name>;
  4. 查看所有表格. show tables;
  5. 查看表格内容. select * from <table_name> where <condition>;
  6. 删除表格内容. delete from <table_name> where <condition>;
  7. 更新表格内容. update <tabele_name> SET <column_name> = <value> where <condition>;

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

Dockerfile 安装conda之后激活环境

dockerfile应用conda环境可以应用已有的一个miniconda image. 在这里

https://hub.docker.com/r/continuumio/miniconda/

在Dockerfile中通过conda安装环境和创建环境后,需要激活该环境时,不能直接运行

RUN conda activate env_name

因为conda activate 是假设你已经在一个shell中了,而Dockerfile文件执行时不存在shell环境。参考这里

I see. Your line RUN conda activate webapp is failing because conda activate only gets hooked in as an interactive shell command when you're actually using a shell. The statement, even if executed correctly, would have no effect, as that state wouldn't be carried over to the next RUN instruction.

https://github.com/ContinuumIO/docker-images/issues/89#issuecomment-408500352

但是我们激活环境的目的一般是为了运行某个应用程序,那么其实我们可以运行conda run -n env_name app_name

来实现此目的。

Once release conda 4.6, you can make use of conda run in your Dockerfile CMD / ENTRYPOINT.

https://github.com/ContinuumIO/docker-images/issues/89#issuecomment-409808454

linux命令之xargs, awk

xargs主要用于从标准输入读取信息给执行的命令,例如:

docker ps -q -a | xargs docker rm

awk是一个可编程的文本处理命令

在awk的命令里,你可以使用变量,逻辑语句,正则表达式等去处理文件中的文本。使用示例:docker rmi $(docker images | grep "" | awk '{print $3}')

https://www.geeksforgeeks.org/awk-command-unixlinux-examples/

操作示例:

How to use xargs


By default xargs reads items from standard input as separated by blanks and executes a command once for each argument. In the following example standard input is piped to xargs and the mkdir command is run for each argument, creating three folders.

echo 'one two three' | xargs mkdir
ls
one two three


How to use xargs with find


The most common usage of xargs is to use it with the find command. This uses find to search for files or directories and then uses xargs to operate on the results. Typical examples of this are removing files, changing the ownership of files or moving files.

find and xargs can be used together to operate on files that match certain attributes. In the following example files older than two weeks in the temp folder are found and then piped to the xargs command which runs the rm command on each file and removes them.

find /tmp -mtime +14 | xargs rm

Dockerfile COPY and ADD

COPY和ADD命令功能作用类似,其最大的差异在于ADD指令可以拷贝url的文件到image上。不过这个功能可以同样通过RUN curl && tar -xvzf 之类的指令实现。ADD最合适的应用场景为image添加本地的压缩文件。

这篇文章建议,绝大多数情况,尽量使用COPY而不是ADD。因为COPY更加直观

https://nickjanetakis.com/blog/docker-tip-2-the-difference-between-copy-and-add-in-a-dockerile

Dockerfile RUN, CMD, and ENTRYPOINT

run, cmd, entrypoint的区别

https://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/

总体来说

RUN, 是用来建立新一层的image,主要用于安装依赖

ENTRYPOINT, 主要用来配置image的可执行环境,相比于CMD,ENTRYPOINT执行的命令不会因为docker run中命令行的参数而不被执行。如果entrypoint的指令格式为[executable param1 param2]形式,它可以接受CMD或者docker run提供的参数。

CMD, 是用来提供默认运行指令或参数的,它提供的指令会被docker run命令提供的指令覆盖