云盘架构设计

这里先讲分布式文件系统和项目中的一些架构分析,关于fastdfs的分析再另外讲解。
项目的设计,一般有以下几步。一是理清思路,可以通过画框图的方法;二是设计数据库,需要用到哪些数据,明确各个表之间的依赖关系,这一步通过框图实现;三是设计接口;四是程序编码,要弄清整个逻辑流程和每个模块的逻辑流程,一般来说,流程基本是先读所接收数据的长度值,从cgi读数据,解析信息,处理逻辑。这里要注意http数据中的body大小不等于上传的文件大小,因为中间还包括自定义协议的其他协议在内;五是配置fastdfs。在大致完成整个项目后,自己还可以进行一些改进,比如注册流程,可以提前连接mysql,保持长连接,类似连接池的做法,缩短注册流程时间。

分布式文件系统

分布式文件系统最主要的特性是能支持水平扩展,通过分多个组实现。
同一组内的storage数据是一样的,组内的多个storage可以提高读的并发量,同时还可以应对单点故障的问题。某一个storage出现问题,在更换磁盘时,可以从组内其他storage获取数据。fastdfs组内storage的数据同步是通过binlog来做的,组内storage不支持动态增加,需要手动配置,每个storage都有自己的配置文件,都需要进行一样的配置,这一点再次提示一次。
当然,不同组间的数据是不一样的。
对于fastdfs而言,一般是单集群,再多配集群,同步的代价会非常高昂。

redis在项目中的作用

存储token

用户在登录时获得一个有时限的随机token值,一个用户名对一个token值,并设置过期时间。以后用户进行操作,我们就检验这个token,而不再每次都检验密码。这里随机生成token值的方法很多,重点是要保证唯一性。

key:⽤户名
value:token

使用有时限嗯token代替密码,有以下几个原因。
一是密码存储在mysql,token存储在redis,使用token就不需要每次都从mysql中读数据,也就是用读内存代替了读磁盘。
二是安全性方面。token即使被截获,因为有过期时限,所以安全性影响不大,如果密码被截获,就会产生较大影响。
三是防盗链。请求数据时,带上token,token超时则返回错误,需要重新登录,token值也会重新生成,这样可以降低盗链带来的影响。
最后再提示一下,token是有时限的,token过期就需要重新用密码登录。

共享⽂件下载排⾏榜

这里使用有序表zset结构

key : filed_id
value: 下载次数

排⾏榜filed_id映射⽂件名是hash结构,这样可以快速获取文件名。

filed_id: md5(⽂件名)+⽂件名
value: ⽂件名

注意文件的md5值与源文件名无关,即使文件名不同,里面的数据相同,md5值还是相同的。

上传文件

1、上传文件太大,最好不要用http传,用append的方式
2、如果多人同时上传大文件,存储空间和性能会影响到并发。因此,fastdfs并不建议传太大文件,建议不超过500M。
3、本项目上传文件的实际步骤,是要先把文件从客户端保存到cgi所在服务器,再上传到fastdfs中,拼接出http地址,再写数据库。这里上传两次,是因为fastdfs提供的文件上传的接口fastdfs_update_file只能这样操作。给出部分源码

/**
 * @brief  解析上传的post数据 保存到本地临时路径
 *         同时得到文件上传者、文件名称、文件大小
 *
 * @param len       (in)    post数据的长度
 * @param user      (out)   文件上传者
 * @param file_name (out)   文件的文件名
 * @param md5       (out)   文件的MD5码
 * @param p_size    (out)   文件大小
 *
 * @returns
 *          0 succ, -1 fail
 */
int recv_save_file(long len, char *user, char *filename, char *md5, long *p_size)
{
    int ret = 0;
    char *file_buf = NULL;
    char *begin = NULL;
    char *p, *q, *k;

    char content_text[TEMP_BUF_MAX_LEN] = {0}; //文件头部信息
    char boundary[TEMP_BUF_MAX_LEN] = {0};     //分界线信息

    //==========> 开辟存放文件的 内存 <===========
    file_buf = (char *)malloc(len);
    if (file_buf == NULL)
    {
        LOG(UPLOAD_LOG_MODULE, UPLOAD_LOG_PROC, "malloc error! file size is to big!!!!\n");
        return -1;
    }

    int ret2 = fread(file_buf, 1, len, stdin); //从标准输入(web服务器)读取内容
/*
中间略去一部分代码
*/
    //=====> 此时begin-->p两个指针的区间就是post的文件二进制数据
    //======>将数据写入文件中,其中文件名也是从post数据解析得来  <===========

    int fd = 0;
    LOG(UPLOAD_LOG_MODULE, UPLOAD_LOG_PROC,"start open %s\n", filename);
    fd = open(filename, O_CREAT|O_WRONLY, 0644);
/*
后面代码略去
*/
}

上面是把读到的数据保存到了服务器本地,下面再上传至fastdfs

/**
 * @brief  将一个本地文件上传到 后台分布式文件系统中
 *
 * @param filename  (in) 本地文件的路径
 * @param fileid    (out)得到上传之后的文件ID路径
 *
 * @returns
 *      0 succ, -1 fail
 */
/* -------------------------------------------*/
int upload_to_dstorage(char *filename, char *fileid)
{
/*
只给出上传至fastdfs的代码
*/
    //通过execlp执行fdfs_upload_file ,如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了.
    execlp("fdfs_upload_file", "fdfs_upload_file", fdfs_cli_conf_path, filename, NULL);
    //这里fdfs_cli_conf_path是client.conf的位置,通过解析cfg.json获得
}

当然,这里并没有完整给出上传文件的逻辑,特别是没有提到秒传文件的逻辑,在上传文件时,会先用md5值进行检验,如果数据库中有相同md5值的文件,只在数据库中增加记录和相应文件的引用计数,不会实际上传文件至fastdfs。
这里注意一点,去重是在数据库、cgi服务器这一层做的,不是在fastdfs中做的,以后还会讲到。

协议设计

C/S交互协议

服务端与客户端之间的交互需要协议设计,有两种类型,一是mem dump形式,即定义一个结构体,把sizeof(结构体)发送出去,fastdfs采用此方式,其实是header+body形式;二是序列化的方式,如json。

文件上传协议

一般有两个必要元素,扩展名和文件大小。扩展名用来得知所存文件类型,文件大小用于提前告知服务器所传文件大小,便于判断和开辟空间。
fastdfs是不会自己存储源文件名的,都是将fileid返回给客户端,客户端想要去找文件,需要自己存储对应的文件名。
这里为什么不存源文件名呢?因为fastdfs中存储了文件id,又因为fastdfs需要与数据库相联,而数据库中有fileid与文件名的映射,就不需要存储源文件名了。fastdfs下载文件也是通过fileid来查找的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值