• docker部署django项目、mysql主从搭建、django实现读写分离


    我们使用python镜像源构建的出的容器,是一个纯净的debian系统下的python环境,在容器内部要想安装vim、nginx等,就不能使用yum install ...了,需要使用apt-get源。首先更新apt-get源,使用apt-get update ,之后就可以使用apt-get install ...了。
    

    docker部署django项目

    1.1 基于python基础镜像

    将本地django项目打包并发到远程服务器上

    本机是Windows系统,现在要将本机的一个django项目部署到远程的服务器上。
    1.先将本地的django文件压缩成.zip格式的压缩包。
    2.使用xshell远程连接服务器,在服务器上的/home/路径下创建一个文件夹名为test_project的文件夹,cd到该文件夹内;
    3.直接将django的压缩包拖到xshell中。# 发现一直上传不成功,上网查询,发现需要在服务器端安装一个包lrzsz,直接使用yum install lrzsz  命令安装,之后再次拖曳就能上传到我们cd的文件夹了,具体如下图。
    

    将服务器上的.zip django项目解压

    安装unzip、和zip 包
    yum install -y unzip zip
    
    # 解压django项目
    unzip  myproject.zip 
    

    部署的具体流程

    # 前台运行一个python容器、端口映射及目录挂载
    docker run -di --name=myproject -p 8080:8080 -v /home/myproject:/home python:3.6
    # 进入容器I 
    docker exec -it myproject /bin/bash
    # 切到项目路径下:安装依赖
    pip install -r requirement.txt -i https://pypi.douban.com/simple/
    # pip list  
    apt-get update
    apt-get vim
    # setting.py 改成下面
    ALLOWED_HOSTS = ['*']
    # 运行项目(wsgiref)
    python manage.py runserver 0.0.0.0:8080
    # 换uwsgi跑
    pip install uwsgi
    # 在项目根路径下创建一个uwsgi.ini 文件,写入
    
    [uwsgi]
    #配置和nginx连接的socket连接
    socket=0.0.0.0:8080
    #也可以使用http
    #http=0.0.0.0:8080
    #配置项目路径,项目的所在目录
    chdir=/home/django_test
    #配置wsgi接口模块文件路径
    wsgi-file=django_test/wsgi.py
    #配置启动的进程数
    processes=4
    #配置每个进程的线程数
    threads=2
    #配置启动管理主进程
    master=True
    #配置存放主进程的进程号文件
    pidfile=uwsgi.pid
    #配置dump日志记录
    daemonize=uwsgi.log
    
    
    #启动,停止,重启,查看
    uwsgi --ini uwsgi.ini #启动
    lsof -i :8001    #按照端口号查询
    ps aux | grep uwsgi   #按照程序名查询
    kill -9 13844       #杀死进程
    uwsgi --stop uwsgi.pid      #通过uwsg停止uwsgi
    uwsgi --reload uwsgi.pid  #重启
    
    # nginx转发
    
    mkdir -p nginx/conf nginx/html nginx/logs
    
    在conf目录下新建nginx.conf
    
    worker_processes  1;
    events {
        worker_connections  1024;
    }
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        server {
            listen       80;
            server_name  localhost;
            location / {
              #uwsgi_pass 101.133.225.166:8080;
              proxy_pass http://101.133.225.166:8080;
            }  
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    
    docker run --name nginx -id -p 80:80 -v /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v /home/nginx/html:/etc/nginx/html -v /home/nginx/logs:/var/log/nginx nginx
    
    # 在 python的docker中用uwsgi跑起项目来即可
    
    外部访问:http://101.133.225.166/
    

    基于wsgiref:

    向django后台发送请求:

    后台的views的 index函数内部print(request.Meta)结果:

    1.2 基于dockerfile

    
    # 第二种方式:dockerfile
    
    # 写一个dockerfile即可
    
    FROM python:3.6
    MAINTAINER zhang
    WORKDIR /home
    RUN pip install django==1.11.9
    RUN pip install uwsgi
    EXPOSE 8080
    CMD ["uwsgi","--ini","/home/django_test/uwsgi.ini"] 
    
    # 这句命令,是后台执行的,不会夯住,容器里面就停了
    
    # dockerfile路径下要有一个django_test.tar
    
    #构建镜像
    docker build -t='django_1.11.9' .
    # 运行容器
    docker run -di --name=mydjango -p 8080:8080 -v /home/myproject:/home django_1.11.9
    
    
    # 以后只需要从git上拉下最新代码,重启,完事(最新代码)
    

    2 mysql主从搭建

    一 主从配置原理

    mysql主从配置的流程大体如图:

    1)master会将变动记录到二进制日志里面;

    2)master有一个I/O线程将二进制日志发送到slave;

    1. slave有一个I/O线程把master发送的二进制写入到relay日志里面;

    4)slave有一个SQL线程,按照relay日志处理slave的数据;

    二 操作步骤

    2.1我们准备三台装好mysql的服务器(我在此用docker模拟了三台机器)

    环境 mysql版本 ip地址:端口号
    主库(master) 5.7 172.16.209.100:3306
    从库(slave1) 5.7 172.16.209.100:3307
    从库(slave2) 5.7 172.16.209.100:3308

    用docker拉起两个mysql容器,步骤如下:

    # 拉取mysql5.7镜像
    docker pull mysql:5.7
        
    # 主库相关----------------------------------------------------------------------
    #在home目录下创建mysql文件夹,下面创建data和conf.d文件夹
    mkdir /home/mysql
    mkdir /home/mysql/conf.d
    mkdir /home/mysql/data/
     
    创建my.cnf配置文件
    touch /home/mysql/my.cnf
    # 主库配置-------------------------------------------------------------------
    my.cnf添加如下内容:
    # 主库配置开始--------------------------------------------------------------
    [mysqld]
    user=mysql
    character-set-server=utf8
    default_authentication_plugin=mysql_native_password
    secure_file_priv=/var/lib/mysql
    expire_logs_days=7
    sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
    max_connections=1000
    ##主库----start--- 同一局域网内注意要唯一
    server-id=100  
    ## 开启二进制日志功能,可以随便取(关键)
    log-bin=mysql-bin
    ##主库----end--- 
    [client]
    default-character-set=utf8
    [mysql]
    default-character-set=utf8
    # 主库配置结束-------------------------------------------------------------
    
    
    
    # 从库相关------------------------------------------------------------------
    #在home目录下创建sla_mysql1文件夹,下面创建data和conf.d文件夹
    mkdir /home/sla_mysql1
    mkdir /home/sla_mysql1/conf.d
    mkdir /home/sla_mysql1/data/
    
    # 从库配置文件
    touch /home/sla_mysql1/my.cnf
    # 从库配置开始-------------------------------------------------------------
    [mysqld]
    user=mysql
    character-set-server=utf8
    default_authentication_plugin=mysql_native_password
    secure_file_priv=/var/lib/mysql
    expire_logs_days=7
    sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
    max_connections=1000
    # 从库----start--- 
    # 设置server_id,注意要唯一
    server-id=101  
    # 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
    log-bin=mysql-slave-bin   
    # relay_log配置中继日志
    relay_log=edu-mysql-relay-bin 
    # 从库----end--- 
    
    [client]
    default-character-set=utf8
    [mysql]
    default-character-set=utf8
    
    # 从库配置结束 ---------------------------------------------------------------------------
    '''
    第二个从库和第一个从库的配置相同,只需要把server-id修改一个即可。
    '''
    #启动主库容器(挂载外部目录,端口映射成33307,密码设置为123456)
    docker run  -di -v /home/mysql/data/:/var/lib/mysql -v /home/mysql/conf.d:/etc/mysql/conf.d -v /home/mysql/my.cnf:/etc/mysql/my.cnf -p 3306:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
    #启动从库容器1(挂载外部目录,端口映射成3307,密码设置为123456)
    docker run  -di -v /home/sla_mysql1/data/:/var/lib/mysql -v /home/sla_mysql1/conf.d:/etc/mysql/conf.d -v /home/sla_mysql1/my.cnf:/etc/mysql/my.cnf -p 3307:3306 --name mysql-slave1 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
    
    #启动从库容器2(挂载外部目录,端口映射成3308,密码设置为123456)
    docker run  -di -v /home/sla_mysql2/data/:/var/lib/mysql -v /home/sla_mysql2/conf.d:/etc/mysql/conf.d -v /home/sla_mysql2/my.cnf:/etc/mysql/my.cnf -p 3308:3306 --name mysql-slave2 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
    

    mysql主从容器启动之后,我们使用本地mysql客户端连接远程的mysql

    2.2 远程连接入主库和从库

    #连接主库
    mysql -h 39.99.186.5 -P 3306 -u root -p123456
    #在主库创建用户并授权
    ##创建test用户
    create user 'test'@'%' identified by '123';
    ##授权用户
    grant all privileges on *.* to 'test'@'%' ;
    ###刷新权限
    flush privileges;
    #查看主服务器状态(显示如下图)
    show master status; 
    

    
    #连接从库
    mysql -h 39.99.186.5 -P 3307 -u root -p123456  # mysql-slave1
    mysql -h 39.99.186.5 -P 3308 -u root -p123456  # mysql-slave2
    #配置详解
    /*
    change master to 
    master_host='MySQL主服务器IP地址', 
    master_user='之前在MySQL主服务器上面创建的用户名', 
    master_password='之前创建的密码', 
    master_log_file='MySQL主服务器状态中的二进制文件名', 
    master_log_pos='MySQL主服务器状态中的position值';
    */
    #两个从库执行以下命令:
    # mysql-slave1
    change master to master_host='39.99.186.5',master_port=3306,master_user='test',master_password='123',master_log_file='mysql-bin.000004',master_log_pos=0;  # 表示从主库binlog日志文件mysql-bin.000004的第几条开始记,上图显示的是736,我们可以设置成736,也可以设置为0.
    
    # mysql-slave2
    change master to master_host='39.99.186.5',master_port=3306,master_user='test',master_password='123',master_log_file='mysql-bin.000004',master_log_pos=0;  # 表示从主库binlog日志文件mysql-bin.000004的第几条开始记,上图显示的是736,我们可以设置成736,也可以设置为0.
    
    #启用从库
    start slave;
    #查看从库状态(如下图)
    show slave statusG  # 不要加分号“;”
    

    image-20191105090050611

    2.3 测试主从同步

    #在主库上创建数据库test
    create database test;
    use test;
    #创建表
    create table user (id int not null,name varchar(100)not null ,age tinyint);
    #插入数据
    insert user (id,name,age) values(1,'xxx',20),(2,'yyy',7),(3,'zzz',23);
    
    #在从库上查看是否同步成功
    #查看数据库
    show database;
    use test1;
    #查看表
    show tables;
    #查看数据
    select * from test1;
    

    2.4 Mysql主从延迟的处理

    MySQL多线程同步-Transfer使用说明

    # 方式一:
    
    一般在往主库增数据的时候,可以给前端返回一个提示信息“创建成功”,然后再让客户端去查询,这个时候主库增加的数据早都已经同步到从库去了。及客户端不会出现查不到的现象。除非网络延迟特别厉害。
    # 方式二:采用多线程
    但主从机制是一样的:
    mysql主从的实现是,mysql master被使用后,其中master后台IO线程会写Binlog;slave有一个Relay Log线程同步binlog日志,同时有另一个Extractor线程会读取相应的Binlog,在Slave进行相应的同样的操作。
    对于主从正常执行,相应的延迟几乎是不存在的。但是在高QPS下,主从同步却出现了比较明显的延迟情况。在PPT介绍中,当master QPS达到1万左右时,Slave重放的QPS却只有2000左右,因此所谓的瓶颈其实是在Binlog日志在slave重放这块。而此处实现是单线程的,因此改进的方法此处用多线程实现。
    
    PPT中介绍淘宝实现,修改了源码,对应的机制是Transfer机制:此处通过对Binlog日志重放采用多线程实现,从而提高slave的QPPS,PPT给出的实验数据按此机制实现后,QPS能达到1万多。从而解决mysql主从之间高QPS下的数据同步问题。
    当然使用此机制对应的mysql相关配制也有一定的要求:
        1.Binlog日志格式必须是基于ROW级别的,
        2.对应的SQL语句必须对应PK或者uni key。
    
    对于以上两点,作者解析是基于多线程必须是知道PK或者uni key才能完成相应的多线程重放slave,这样才能保证SQL语句的执行顺序。相对来说,对于第二点很多应用需求还是能满足。但是对ROW日志格式,对于一些批量修改等应用,采用此日志格式所带来的DB的IO压力等应该也是需要考虑的。
    
    毕竟一种新的方案的提出有它的优点就有它的缺点,合适才是最合理的。
    

    2.5 django实现读写分离

    创建django项目,在settings文件配置主从同步的多个数据库。

    import pymysql
    pymysql.install_as_MySQLdb()
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
             'HOST':'39.99.186.5',
             'NAME': 'test',
             'USER':'root',
             'PASSWORD':'123456',
             'PORT':3306,
             'CHARSET':'utf8'
        },
    
        'db1': {
                'ENGINE': 'django.db.backends.mysql',
                 'HOST':'39.99.186.5',
                 'NAME': 'test',
                 'USER':'root',
                 'PASSWORD':'123456',
                 'PORT':3307,
                 'CHARSET':'utf8'
        },
        'db2': {
                    'ENGINE': 'django.db.backends.mysql',
                     'HOST':'39.99.186.5',
                     'NAME': 'test',
                     'USER':'root',
                     'PASSWORD':'123456',
                     'PORT':3308,
                     'CHARSET':'utf8'
            }
    }
    '''
    注意:以上三个mysql做了主从复制搭建,在使用migrate向数据库迁移表文件时,默认是迁移到default上,由于做了主从,从库也会从主库同步表文件数据。,如果没有主从的话,表迁移默认只会在default中生成表
    '''
    

    如何指定迁移到哪个数据库?

    手动操作:

    migrate app01 --database=db1;
    # 多数据库:
    python migrate manage.py makemigrations
    python migrate manage.py migrate app名称  --database=配置文件数据名称的别名
    
    # 1.手动操作
    models.User.objects.using('db1').create(title='普通用户')
    result = models.User.objects.all().using('default')
    

    自动操作:

    在小django_test文件夹下创建db_router.py文件

    # 自动操作 
    class Router1:  
        # 指定读的数据库
        def db_for_read(self, model, **hints):
            # return 'db1'
            
            # 一主两从的情况下
            read_database = ['db1','db2']
            return read_database
        
        # 指定写的数据库
        def db_for_write(self, model, **hints):
            return 'deafult'
    

    django_test/settings.py

    # 在settings.py中配置Route1类  
    DATABASE_ROUTERS = ['django_test.db_router.Router1']
    

    使用示例:

    app01/views.py

    from django.shortcuts import render, HttpResponse
    from app01 import models
    def index(request,*args, **kwargs):
    
        # 方式一:手动指定往哪个数据库读写
    
        # # 写数据,在主库写
        # book_obj = models.Book.objects.create(name='西游记')
        # print(book_obj)
        #
        # # 查数据,去从库查  手动指定去哪个数据库读
        # book_queryset = models.Book.objects.all().using('db1')
        # print(list(book_queryset))
    
    
        # 方式二:自动去我们配置类指定的数据库进行读写
    
        # 自动去从库读数据
        book_queryset = models.Book.objects.all()
        print(book_queryset)
        book_obj = models.Book.objects.create(name='红楼梦')
        return HttpResponse('新增成功!')
    
    

    在url.py中做好路由匹配:

    from app01 import views
    urlpatterns = [
        path('index/', views.index),
    ]
    

    浏览器访问index/

  • 相关阅读:
    zookeeper java调用及权限控制
    走进C++程序世界------IO标准库介绍
    Unity3d 镜面折射 vertex and frag Shader源代码
    servlet上传文件报错(二)
    模仿linux内核定时器代码,用python语言实现定时器
    6.12交流
    python学习(三) 使用字符串
    JAVA面试(四)
    python学习(二) 列表和元组
    C++面试题(三)
  • 原文地址:https://www.cnblogs.com/zhangchaocoming/p/12683505.html
Copyright © 2020-2023  润新知