文件上传

FastDFS

1,开源的轻量级分布式文件系统,通过集群扩展用于解决大数据量存储和负载均衡等问题。支持HTTP协议传输文件(结合Nginx);对文件内容做Hash处理,节约磁盘空间;通过Nginx多tracker、多group、group下多存储主机实现负载均衡,整体性能较佳。适用中小型系统。

2,FastDFS的二个角色:跟踪服务器(Tracker)、存储服务器(Storage)。跟踪服务器:主要做调度工作,起到负载均衡的作用。它是客户端和存储服务器交互的枢纽,将数据请求落到某个group上。存储服务器:主要提供容量和备份服务,存储服务器是以组(Group)为单位,每个组内可以有多台存储服务器,数据互为备份。文件及属性(Meta Data)都保存在该服务器上。

image-20220414203904545

3,安装

将上述配置好的容器提交为镜像。

4,启动服务

5,测试

代理

1,正向代理的特点:正向代理是代理客户端,为客户端收发请求,使真实客户端对服务器不可见。服务端不知道客户端、客户端知道服务端,隐藏真实客户端。为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。由于代理对客户端是可感知的,所以必须要进行一些特别的设置才能使用正向代理。 优点:为在防火墙内的局域网客户端提供访问 Internet 的途径。可以做缓存,加速访问资源。对客户端访问授权,上网进行认证。代理可以记录用户访问。记录(上网行为管理),对外隐藏用户信息。

17125abbcc1b1001_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0

2,反向代理的特点:反向代理是代理服务器端,为服务器收发请求,使真实服务器对客户端不可见。服务端知道客户端、客户端不知道服务端,隐藏真实服务端。客户端请求会先被代理端处理,代理端从另外一台服务器上取回来,然后作为自己的内容吐给用户,客户端对上述流程未知,对于客户端而言代理端就像是原始服务器,并且客户端不需要进行任何特别的设置。

优点:在计算机世界里,由于单个服务器的处理客户端(用户)请求能力有一个极限,当用户的接入请求蜂拥而入时,会造成服务器忙不过来的局面,可以使用多个服务器来共同分担成千上万的用户请求,这些服务器提供相同的服务,对于用户来说,根本感觉不到任何差别。通过将反向代理作为公网访问地址,Web 服务器是内网,由代理端实现请求转发,优化网站的负载,保证内网的安全,隐藏和保护原始服务器。当新加入/移走服务器时,仅仅需要修改负载均衡的服务器列表,而不会影响现有的服务。

Nginx是反向代理服务器。代理其实就是中间人,客户端通过代理发送请求到互联网上的服务器,从而获取想要的资源。实现反向代理、负载均衡。

17125abbccba1a78_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0

3,fastdfs代理流程:外界访问http://外网IP:8888/group1/MM0/dasfarwfddfd.mp4,其中外网IP:8888为nginx地址,而不是直接访问DFS系统,group1/MM0/dasfarwfddfd.mp4也不在代理端上,nginx从多个tracker选择一个tracker转发请求,实现第一层负载均衡,tracker再从多个group下选择一个group实现第二重负载均衡。

image-20220414212500186

image-20220414213418361

负载均衡

1,内置均衡策略:

2,扩展均衡策略:

3,健康检查

如果后端某台服务器响应失败,nginx会标记该台服务器失效,在特定时间内,请求不分发到该台上。

分片传输

文件整体传输时,如果文件较大,则会长时间占用网络带宽,挤占其它应用网络;同时当传输被迫中断时,之前传输数据将被丢弃,并从头开始传输。通过对文件分片,按照一定间隔传输分片,不会长时间占据带宽,并且如果传输中断,只会从被中断分片开始传输,之前分片不会重复传输。上传第一个分片时会创建一个新的文件,并返回访问路径,上传后续分片时,路径和偏移量,直接在原始文件的后面添加。分片任务由客户端完成。

在对文件分片上传时,需要暂存文件路径和已上传比特数用于将后续分片添加到初始分片文件的尾部,以及当已上传分片数与总分片书相同时,表示数据已传输完成,返回文件路径,每当完成一个分片的传输,都要更新已上传比特数以及已上传分片数。由于这些数据都是临时数据,所以使用redis暂存,key为存储信息种类+文件数值摘要构成,保证唯一性。

秒传

对要保存的完整文件生成MD5摘要,MD5摘要之和文件内容相关,与文件名无关,将生成的摘要与已经保存过的文件的摘要进行对比,如果存在匹配项则表示相同文件已经存在,不必重复上传,直接返回已保存文件路径即可,防止保存重复数据。如果不存在相同摘要的文件,则表示是一个新文件,在保存完文件之后一个分片后,将该文件的路径、摘要保存,用于后续文件的摘要比较。

数据表保存文件上传者、路径、md5摘要等信息,由于md5经常被查询,所以建立索引以加快查询速度。

在开始存储分片数据前,先去数据库中查询是否存在与当前完整数字摘要相匹配的数据项,如果存在表示相同文件已保存过,直接返回查询到路径,如果不存在匹配项,则将分片数据保存到FastDFS中,当最后一个分片保存完成后,把文件上传者、路径、md5摘要等信息存入数据库,用于以后秒传时的匹配查找。

以上传一个254MB视频为例,分片上传完成后返回文件在存储服务器上的相对地址,耗时10.90S。

image-20220422172604397

再次上传相同文件时,由于文件md5相同,直接返回该md5对应文件的相对路径,不用上传文件,整个过程耗时158ms。

image-20220422173251339

视频

视频上传

1,数据表设计

t_video视频表存储视频基本信息,包括视频上传者ID、在文件服务器中链接(由文件上传得到)、标题、视频分区(鬼畜、音乐、电影),由于后续会经常对某个用户上传的视频、根据视频标题、视频分区进行查询,所以对userIdtitlearea建立索引,同时对userId建立外键约束。

t_tag表存储视频可选的标签,如二刺螈、老八、孙笑川等。后续可以根据历史观看视频的标签,向用户推送他可能感兴趣的拥有相同标签的视频。由于视频标签类似于公共属性,可以被多用户共享,不想之前的用户关注分组信息那样属于个人独有信息,所以t_tag表不用存储标签的所有者。

t_video_tag用于存储视频与标签对应的关系。因为经常需要根据视频ID查询查询视频对应的标签ID,所以建立联合索引(videoId,tagId),直接获取的tagId避免回表,加快查询速度。同时加上外键约束。

2,上传

客户端先将文件分片上传到文件服务器,获得文件在存储服务器中路径。然后构建视频Bean包含上传者ID、视频路径、视频分区、视频标签、标签等信息,服务端再保存这些信息。服务端先保存视频信息,再根据回填的视频ID信息,保存频标签信息。

查找

根据视频类别查询视频,用于首页的分类别展示。

流程与之前根据昵称分组查询用户相似,先构建分页查询对应的起始位置和每页查询条目数,再查询复合条件的视频条目数,当结果数目大于0时再进行分页查询。

视频播放

在线播放视频与下载视频相似,在线播放最简单的形式就是直接向客户端发送完整链接地址,其缺点是向外界暴露了文件所处位置http://localhost:8888/group1/M00/00/00/CsYy72JidI6EeNK7AAAAAJUikZg655.mp4,用户可以绕过登录等权限控制,直接访问视频文件,同时如果是在下载场景下,传输终端将导致从头开始传输数据。

image-20220415163129099

在线播放建议采用分片传输比特流的形式,客户端向服务器发起对某一个分段数据获取请求,http://localhost:9191/video-slices?url=M00/00/00/rBEABGJZKpCEaoV6AAAAAJUikZg683.mp4,其中相对地址指明的视频并不在localhost上(此处演示为单机,处理请求的服务器和文件存储服务器都是本地主机,可以拓展为多机场景),服务端接受请求再去文件存储服务器查找文件,最终以比特流的形式将数据写入http响应,这样外界无法知道文件具体位置,对用户而言数据就保存在localhost,避免文件暴露,同时分片的方式使得断点续传得以实现。

分片请求时客户端通过相对路径指明要获取的视频,并请求头中通过<Range,bytes=start_byte-end_byte>,指明当前分片请求的开始和结束比特数。服务端通过对存储服务器地址和文件相对路径的拼接获得完整路径,并从存储服务器中读取指定比特数,写入http响应。同时向响应头中写入数据,对返回的比特流进行说明描述:<Content-Range, "bytes+start_byte-end_byte+/+total_size><Accept-Ranges, bytes><Content-Type, video/mp4>,设置内容长度为分片长度,响应码为HttpServletResponse.SC_PARTIAL_CONTENT表示返回部分内容。

读取文件流时先构建http连接,前客户端请求头参数填入,从与文件储存服务器建立的连接获得输出流,读取数据从将要返回的响应获取输出流,写入数据,完成文件比特流的传输。

分片播放效果如下:

image-20220416122534733

点赞

t_video_like保存用户的点赞记录,关联用户ID与被点赞的视频的ID,由于需要查询某个用户点赞了哪些视频,索引建立联合索引(userId,videoId),避免回表,加快查询数据。

客户端传递要点赞的视频ID,后端一次验证视频ID是否合法,用户是否已经点赞过该视频,通过后往`t_video_like添加点赞记录。

取消点赞、查看视频被点赞数、查看用户点赞过的视频依据数据表实现即可,不再赘述。

投币

1,数据表设计

t_video_coin 保存用户视频投币记录,包含用户ID,视频ID,投币数。由于经常查询某用户投币了哪些视频,所以建立联合索引( userId,videoId),避免回表加快查询。由于一行记录包含两个其它表的主键,所以对userIdvideoId添加外键约束。t_user_coin保存用户的硬币账户余额,由于经常查询某用户的账户余额,所以建立联合索引( userId,amount),避免回表加快查询,同时对userId添加外键约束。

每个用于对一个视频最多投两枚硬币,可以一次投两枚,也可以分两次投。投币时先检查余额是否充足,并保证当前投币完成后,该用户对该视频投币总数小于等于2,更新投币记录条目,最后更新账户余额。

查看视频被投币总数、查看用户投币过的视频依据数据表实现即可,不再赘述。

收藏

1,数据表设计

t_collection_group保存用户建立的收藏分组,由于收藏分组为用户私有,需要将分组信息与用户关联,同时默认分组为用户共享,预先插入一条不予用户关联的默认分组。同时对 userId建立索引与外键约束。

image-20220415180332710

t_video_collection保存收藏记录,包括用户ID、被收藏视频ID和收藏分组,由于经常查询某用户收藏视频,所以建立联合索引( userId,videoId),避免回表加快查询。由于一行记录包含三个其它表的主键,所以对groupIduserIdvideoId添加外键约束。

添加收藏、删除收藏、查询收藏过视频等功能频依据数据表实现即可,不再赘述。

评论

1,数据表设计

t_video_comment存储评论信息。包括被评论视频ID、发表评论的用户ID、评论本身、本条评论回复的用户、以及评论所在评论组的根评论对应的用户,由于设计到多个表的主键,所以对userIdvideoId添加外键约束。多用户回复是一个嵌套的树形结构,B站为简化存储使用两次存储结构,第一级为根评论,第二季为由根评论衍生的评论。

image-20220416003454316

比如视频下初始评论A,之后用户回复了评论A,得到评论B,之后用户回复了评论B,得到评论C

对于评论A,由于是最顶层评论,所以replyUserIdrootId都为空;对于评论B,由于是第二层评论,所以replyUserIdrootId都为A对应用户;对于评论C,由于是第三层评论,所以replyUserIdB对应用户、rootIdA对应用户。

2,分页查询评论

由于使用两级存储体系,返回给前端数据需要以根评论为单位,每个根评论内部保存其对应的子评论,根评论本身保存:评论文本、发表评论的用户信息、被评论视频ID,子评论除了根评论保存的信息,还包含回复的用户的信息和根评论ID。

获取视频信息

视频信息包含:视频标题、上传时间、分区、标签、简介、在存储服务器上的相对路径,以上信息由Video封装;点赞数、投币数、收藏数,需要到数据库中查询;视频上传者信息使用UserInfo封装。

image-20220416003403442

返回给前端信息如下: