Django项目实战——11—(文件存储方案FastDFS、容器化方案Docker)

准备知识

  • 虚拟机VMware15中安装Ubuntu18.04步骤
    https://blog.csdn.net/qq_33287871/article/details/99212352
  • Ubuntu18镜像文件
    链接:https://pan.baidu.com/s/1Lr9HozYHYTZCRnnv4tI1pg 提取码:6ht6
  • Ubuntu 16.04安装docker详细步骤
    https://blog.csdn.net/jinking01/article/details/82490688
  • Xshell连接不上VM的Ubuntu18
    sudo apt-get install openssh-server
  • 在Ubuntu上安装Docker Engine(官网)
    https://docs.docker.com/engine/install/ubuntu/
  • Ubuntu:使用Docker安装``FastDFS
    https://blog.csdn.net/weixin_44673477/article/details/88132623
  • Docker下载镜像太慢问题
    https://www.cnblogs.com/spll/p/11828193.html
  • ubuntu18.04配置镜像源
    https://blog.csdn.net/qq_41822647/article/details/85122467
  • Windows的Python环境引入fdfs_client-py包遇到的问题
    https://blog.csdn.net/mingyuli/article/details/104171195
  • Fastdfs客户端ModuleNotFoundError: No module named 'mutagen._compat'问题
    https://www.cnblogs.com/sewen-H/p/13362598.html

商品模型文件apps/goods/models.py

"""
商品模型文件:apps/goods/models.py
"""
from django.db import models
from utils.models import BaseModel


class GoodsCategory(BaseModel):
    """商品类别"""
    name = models.CharField(max_length=10, verbose_name='名称')
    parent = models.ForeignKey('self', related_name='subs', null=True, blank=True, on_delete=models.CASCADE, verbose_name='父类别')

    class Meta:
        db_table = 'tb_goods_category'
        verbose_name = '商品类别'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class GoodsChannelGroup(BaseModel):
    """商品频道组"""
    name = models.CharField(max_length=20, verbose_name='频道组名')

    class Meta:
        db_table = 'tb_channel_group'
        verbose_name = '商品频道组'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class GoodsChannel(BaseModel):
    """商品频道"""
    group = models.ForeignKey(GoodsChannelGroup, verbose_name='频道组名', on_delete=models.CASCADE)     # on_delete=models.CASCADE外键的级联删除
    category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='顶级商品类别')
    url = models.CharField(max_length=50, verbose_name='频道页面链接')
    sequence = models.IntegerField(verbose_name='组内顺序')

    class Meta:
        db_table = 'tb_goods_channel'
        verbose_name = '商品频道'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.category.name


class Brand(BaseModel):
    """品牌"""
    name = models.CharField(max_length=20, verbose_name='名称')
    logo = models.ImageField(verbose_name='Logo图片')
    first_letter = models.CharField(max_length=1, verbose_name='品牌首字母')

    class Meta:
        db_table = 'tb_brand'
        verbose_name = '品牌'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class SPU(BaseModel):
    """商品SPU"""
    name = models.CharField(max_length=50, verbose_name='名称')
    brand = models.ForeignKey(Brand, on_delete=models.PROTECT, verbose_name='品牌')
    category1 = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, related_name='cat1_spu', verbose_name='一级类别')
    category2 = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, related_name='cat2_spu', verbose_name='二级类别')
    category3 = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, related_name='cat3_spu', verbose_name='三级类别')
    sales = models.IntegerField(default=0, verbose_name='销量')
    comments = models.IntegerField(default=0, verbose_name='评价数')
    desc_detail = models.TextField(default='', verbose_name='详细介绍')
    desc_pack = models.TextField(default='', verbose_name='包装信息')
    desc_service = models.TextField(default='', verbose_name='售后服务')

    class Meta:
        db_table = 'tb_spu'
        verbose_name = '商品SPU'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class SKU(BaseModel):
    """商品SKU"""
    name = models.CharField(max_length=50, verbose_name='名称')
    caption = models.CharField(max_length=100, verbose_name='副标题')
    spu = models.ForeignKey(SPU, on_delete=models.CASCADE, verbose_name='商品')
    category = models.ForeignKey(GoodsCategory, on_delete=models.PROTECT, verbose_name='从属类别')
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='单价')
    cost_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='进价')
    market_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='市场价')
    stock = models.IntegerField(default=0, verbose_name='库存')
    sales = models.IntegerField(default=0, verbose_name='销量')
    comments = models.IntegerField(default=0, verbose_name='评价数')
    is_launched = models.BooleanField(default=True, verbose_name='是否上架销售')
    default_image_url = models.CharField(max_length=200, default='', null=True, blank=True, verbose_name='默认图片')

    class Meta:
        db_table = 'tb_sku'
        verbose_name = '商品SKU'
        verbose_name_plural = verbose_name

    def __str__(self):
        return '%s: %s' % (self.id, self.name)


class SKUImage(BaseModel):
    """SKU图片"""
    sku = models.ForeignKey(SKU, on_delete=models.CASCADE, verbose_name='sku')
    image = models.ImageField(verbose_name='图片')

    class Meta:
        db_table = 'tb_sku_image'
        verbose_name = 'SKU图片'
        verbose_name_plural = verbose_name

    def __str__(self):
        return '%s %s' % (self.sku.name, self.id)


class SPUSpecification(BaseModel):
    """商品SPU规格"""
    spu = models.ForeignKey(SPU, on_delete=models.CASCADE, related_name='specs', verbose_name='商品SPU')
    name = models.CharField(max_length=20, verbose_name='规格名称')

    class Meta:
        db_table = 'tb_spu_specification'
        verbose_name = '商品SPU规格'
        verbose_name_plural = verbose_name

    def __str__(self):
        return '%s: %s' % (self.spu.name, self.name)


class SpecificationOption(BaseModel):
    """规格选项"""
    spec = models.ForeignKey(SPUSpecification, related_name='options', on_delete=models.CASCADE, verbose_name='规格')
    value = models.CharField(max_length=20, verbose_name='选项值')

    class Meta:
        db_table = 'tb_specification_option'
        verbose_name = '规格选项'
        verbose_name_plural = verbose_name

    def __str__(self):
        return '%s - %s' % (self.spec, self.value)


class SKUSpecification(BaseModel):
    """SKU具体规格"""
    sku = models.ForeignKey(SKU, related_name='specs', on_delete=models.CASCADE, verbose_name='sku')
    spec = models.ForeignKey(SPUSpecification, on_delete=models.PROTECT, verbose_name='规格名称')
    option = models.ForeignKey(SpecificationOption, on_delete=models.PROTECT, verbose_name='规格值')

    class Meta:
        db_table = 'tb_sku_specification'
        verbose_name = 'SKU规格'
        verbose_name_plural = verbose_name

    def __str__(self):
        return '%s: %s - %s' % (self.sku, self.spec.name, self.option.value)

映射数据库:
在这里插入图片描述
插入数据到表中
在这里插入图片描述

1、文件存储方案FastDFS

FastDFS介绍

• 用c语言编写的一款开源的轻量级分布式文件系统
• 功能包括:文件存储、文件访问(文件上传、文件下载)、文件同步等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等
• 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标
• 可以帮助我们搭建一套高性能的文件服务器集群,并提供文件上传、下载等服务

在这里插入图片描述

  • FastDFS架构包括ClientTracker serverStorage server
    Client请求Tracker进行文件上传、下载,Tracker再调度Storage完成文件上传和下载。
  • Client: 客户端,业务请求的发起方,通过专有接口,使用TCP/IP协议TrackerStorage进行数据交互。FastDFS提供了uploaddownloaddelete等接口供客户端使用。
  • Tracker server:跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所有存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽。
  • Storage server:存储服务器(存储节点或数据服务器),文件和文件属性都保存到存储服务器上。Storage server直接利用OS的文件系统调用管理文件。
    Storage群中的横向可以扩容,纵向可以备份

FastDFS上传和下载流程

上传流程
在这里插入图片描述
下载流程
在这里插入图片描述

FastDFS文件索引

  • FastDFS上传和下载流程可以看出都涉及到一个数据叫文件索引(file_id)
    文件索引(file_id)是客户端上传文件后Storage返回给客户端的一个字符串,是以后访问该文件的索引信息。
  • 文件索引(file_id)信息包括:组名、虚拟磁盘路径、数据两级目录、文件名等信息
    组名:文件上传后所在的 Storage 组名称。
    虚拟磁盘路径:Storage 配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0则是M00,如果配置了store_path1则是M01,以此类推。
    数据两级目录:Storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
    文件名:由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息.

在这里插入图片描述
在这里插入图片描述

2、容器化方案Docker

  • FastDFS的安装步骤非常的多,涉及的依赖包也很多,当新的机器需要安装FastDFS时,是否需要从头开始安装。
  • 在工作中,如何高效的保证开发人员写代码的开发环境与应用程序要部署的生产环境一致性。如果要部署一台新的机器,是否需要从头开始部署。

结论

  • 上述思考的问题,都涉及到相同的工作是否需要重复做
  • 避免相同的工作重复做是容器化技术应用之一

容器化方案

  • Docker
    Docker的目标之一就是缩短代码从开发、测试到部署、上线运行的周期,让我们的应用程序具备可移植性、易于构建、并易于协作。

Docker介绍
• Docker中文社区文档:https://www.docker.org.cn/index.html
• Docker 是一个开源的软件部署解决方案。
• Docker 也是轻量级的应用容器框架。
• Docker 可以打包、发布、运行任何的应用。
• Docker 就像一个盒子,里面可以装很多物件,如果需要某些物件,可以直接将该盒子拿走,而不需要从该盒子中一件一件的取。
• Docker 是一个客户端-服务端(C/S)架构程序。
客户端只需要向服务端发出请求,服务端处理完请求后会返回结果

Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、 bare metalOpenStack 集群和其他的基础应用平台。

Docker通常用于如下场景

  • web应用的自动化打包和发布;
  • 自动化测试和持续集成、发布;
  • 在服务型环境中部署和调整数据库或其他的后台应用;
  • 从头编译或者扩展现有的OpenShiftCloud Foundry平台来搭建自己的PaaS环境。

Docker 包括三个基本概念:

  • 镜像(Image)
    • Docker的镜像概念类似于虚拟机里的镜像,是一个只读的模板,一个独立的文件系统,包括运行容器所需的数据,可以用来创建新的容器。
    • 例如:一个镜像可以包含一个完整的 ubuntu 操作系统环境,里面仅安装了MySQL或用户需要的其它应用程序。
  • 容器(Container)
    • Docker容器是由Docker镜像创建的运行实例,类似VM虚拟机,支持启动,停止,删除等。
    每个容器间是相互隔离的,容器中会运行特定的应用,包含特定应用的代码及所需的依赖文件。
  • 仓库(Repository) https://hub.docker.com/
    • Docker的仓库功能类似于Github,是用于托管镜像的。

Docker安装(ubuntu 16.04)

  1. 源码安装Docker CE(另一种安装方式在本博客的最上方准备知识)
cd docker源码目录
sudo apt-key add gpg
sudo dpkg -i docker-ce_17.03.2~ce-0~ubuntu-xenial_amd64.deb
  1. 检查Docker CE是否安装正确
sudo docker run hello-world

出现如下信息,表示安装成功
在这里插入图片描述
3. 启动与停止
• 安装完成Docker后,默认已经启动了docker服务。安装其他模块时候会出现占用的错误,需要将进程断开。

# 启动docker
sudo service docker start
# 重启docker
sudo service docker restart
# 停止docker
sudo service docker stop

Docker镜像操作

  1. 镜像列表
sudo docker image ls

在这里插入图片描述

* REPOSITORY:镜像所在的仓库名称 
* TAG:镜像标签 
* IMAGEID:镜像ID 
* CREATED:镜像的创建日期(不是获取该镜像的日期) 
* SIZE:镜像大小
  1. 从仓库拉取镜像
# 官方镜像
sudo docker image pull 镜像名称 或者 sudo docker image pull library/镜像名称
sudo docker image pull ubuntu 或者 sudo docker image pull library/ubuntu
sudo docker image pull ubuntu:16.04 或者 sudo docker image pull library/ubuntu:16.04
# 个人镜像
sudo docker image pull 仓库名称/镜像名称
sudo docker image pull xx/fastdfs
  1. 删除镜像
sudo docker image rm 镜像名或镜像ID
sudo docker image rm hello-world
sudo docker image rm fce289e99eb9

在这里插入图片描述

Docker容器操作

  1. 容器列表
# 查看正在运行的容器
sudo docker container ls
# 查看所有的容器
sudo docker container ls --all
  1. 创建容器,此时的容器还没有run
sudo docker run [option] 镜像名 [向启动容器中传入的命令]
option 常用可选参数说明:
* -i 表示以《交互模式》运行容器。
* -t 表示容器启动后会进入其命令行。加入这两个参数后,容器创建就能登录进去。即分配一个伪终端。
* --name 为创建的容器命名。
* -v 表示目录映射关系,即宿主机目录:容器中目录。注意:最好做目录映射,在宿主机上做修改,然后共享到容器上。 
* -d 会创建一个守护式容器在后台运行(这样创建容器后不会自动登录容器)* -p 表示端口映射,即宿主机端口:容器中端口。
* --network=host 表示将主机的网络环境映射到容器中,使容器的网络与主机相同。
  1. 交互式容器
sudo docker run -it --name=容器命名 ubuntu /bin/bash
在容器中可以随意执行linux命令,就是一个ubuntu的环境。
当执行 exit 命令退出时,该容器随之停止。

在这里插入图片描述

  1. 守护式容器
# 开启守护式容器
sudo docker run -dit --name=ubuntu2 ubuntu
# 进入到容器内部交互环境
sudo docker exec -it 容器名或容器id 进入后执行的第一个命令
sudo docker exec -it ubuntu2 /bin/bash

**如果对于一个需要长期运行的容器来说,我们可以创建一个守护式容器。**守护式容器在后台运行。

在容器内部执行 exit 命令退出时,该容器也随之停止。
  1. 停止和启动容器
# 停止容器
sudo docker container stop 容器名或容器id
# kill掉容器
sudo docker container kill 容器名或容器id
# 启动容器
sudo docker container start 容器名或容器id
  1. 删除容器
    正在运行的容器无法直接删除。
sudo docker container rm 容器名或容器id
  1. 容器制作成镜像
    为保证已经配置完成的环境可以重复利用,我们可以将容器制作成镜像
# 将容器制作成镜像
sudo docker commit 容器名 镜像名

# 镜像打包备份
sudo docker save -o 保存的文件名 镜像名

# 镜像解压
sudo docker load -i 文件路径/备份文件

保存的文件发送到其他机器上之后,利用镜像解压就可以直接使用了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值