Skip to content

Latest commit

 

History

History
193 lines (168 loc) · 16.9 KB

README.md

File metadata and controls

193 lines (168 loc) · 16.9 KB

一、项目介绍

项目介绍

本次项目我们小组使用Go语言中Gin框架实现了极简版抖音的后端搭建。 选择使用Gin框架的原因: 1、小组内除唐振宇外,三人均无Go语言基础与开发经验,需要从头开始学习Go语言。 2、在录播课中,Web三件套教学时间较短且Hertz框架文档并不详细,故使用生态比较完善、文档更广泛更全面的框架Gin。

项目服务地址

项目已部署于服务器,服务地址为:https://tiktok.hymsk.asia/ 静态资源访问地址为:https://static.tiktok.hymsk.asia/

Github地址

项目Github地址为:https://github.com/Hua-ymsk/TikTok/

二、项目分工

团队成员 主要贡献
凌鑫杰 队长,管理日常团队协作与总体项目进度,负责服务器配置与项目整体设计与项目部署,数据库设计以及演示视频的录制剪辑,主要负责接口:关注操作、用户关注列表、用户粉丝列表、用户好友列表。
唐振宇 队员,辅助整体架构搭建,初步实现项目所需中间件,主要负责接口:视频流、发布视频、发布列表
魏海林 队员,整体架构设计,项目的优化以及拓展,项目中间件的设计优化,接口测试,主要负责接口:用户登录,用户注册,获取用户信息。
郑浩天 队员,辅助整体架构搭建,分析安全问题并给出解决方案,项目功能测试,主要负责接口:赞操作、评论操作、发送消息操作、喜欢列表、视频评论列表、聊天记录

三、项目实现

3.1 技术选型与相关开发文档

硬件环境

  • 开发环境(基本情况,不同队员软硬件环境可能有一定不同)

硬件环境:CPU:Intel i7-10875H 2.30GHz,16G RAM,1T SSD 软件环境:Windows10 64位、Visual Studio Code、GoLand、go1.19.5 windows/amd64

  • 生产环境

硬件环境:CPU:Intel(R) Xeon(R) 2.50GHz,4G RAM,80G SSD 软件环境:CentOS 7.6、Nginx 1.6.2、MySQL 8.0.16、go1.19.5 linux/amd64

软件环境

  • Golang
    • Gorm
      • gorm是由go编写,在github上活跃度很高的一个对象关系映射库,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了,有助于编码效率提升 。gorm还支持预加载、事务、库自动迁移等功能。
    • jwt-go
      • jwt(json web token)是一种用于前后端身份认证的方法,一个jwt由header,payload,和signature组成。header:包含了token类型和算法类型;payload:包含了一些用户自定义或jwt预定义的一些数据;signature:将header和payload经过base64编码,加上一个secret密钥,整体经过header中的算法加密后生成。将jwt认证作为路由中间件注册,即可在处理请求前解析请求携带的token,获取user_id,并设置到上下文,便于之 后业务进行。
    • zap
      • 一个好的日志记录器应具有以下功能: 能够将事件记录到文件中,而不是应用程序控制台。 日志切割-能够根据文件大小、时间或间隔等来切割日志文件。 支持不同的日志级别。例如INFO,DEBUG,ERROR等。 能够打印基本信息,如调用文件/函数名和行号,日志时间等。 本项目将zap logger作为全局中间件注册,既可以在panic时自动记录函数调用栈,帮助定位bug,也可 以主动调用记录错误
  • MySQL MySQL是一个关系型数据库管理系统, 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。由于其体积小、速度快、总体拥有成本低,尤其是开源这一特点,故在本项目中使用MySQL作为项目数据库,具体数据库设计详见——3.3 数据库设计部分
  • Nginx Nginx 是一个高性能的 HTTP 和反向代理 Web 服务器,其与传统 Web 服务器相比速度更快、并发更高,单请求或者高并发请求的环境下,Nginx 都会比其他 Web 服务器响应的速度更快,配置简单,扩展性强,具有高可靠性的特点。 在本次项目实践中,使用Nginx作为代理服务器,进行配置SSL证书与域名访问,实现反向代理并作为静态资源服务器处理静态资源请求(提供视频服务)。
  • ffmpeg FFmpeg是一套可以用来记录、转换数据音频、视频,并能将其转化为流的开源计算机程序。本项目使用ffmpeg来获取视频第一帧截图作为封面,保存到服务器上,再用nginx反向代理供外部访问。

3.2 项目代码介绍

数据库设计详细说明

根据E-R图,结合项目具体需求,本项目一共设计6张表,分别为users表、videos表、comments表、likes表、follows表、chats表。 由于本项目体量小,数据库较为简单,在数据库设计中,我们采用了使用触发器进行相对应表中对应数量字段的加减,进行级联更新,可以防止由于后端代码逻辑问题造成的一些错误,更快更高效的维护数据。 表相关具体设计如下:

users表:用户基本信息表
字段 说明
id 自增主键
user_name 唯一键,用户名
password 使用MD5码进行加密加盐后得到的用户密码
nickname 昵称
fans 粉丝数
follows 关注数
videos表:视频基本信息表
字段 说明
id 自增主键
user_id 外键(依赖于users.id),发布视频的用户id
video_url 视频播放URL
cover_url 视频封面URL
timestamp 视频发布时间戳
title 视频标题
likes_num 视频点赞数
comments_num 视频评论数
comments表:视频评论表
字段 说明
id 自增主键
user_id 外键(依赖于users.id),发布评论的用户id
video_id 外键(依赖于videos.id),评论视频的视频id
timestamp 视频发布时间戳
content 评论内容(内容小于1000字符)

触发器:当comments表插入/删除时修改videos表对应视频comments_num字段

likes表:视频点赞表
字段 说明
id 自增主键
user_id 外键(依赖于users.id),点赞的用户id
video_id 外键(依赖于videos.id),被点赞视频的视频id
联合唯一索引 user_id&video_id

触发器:当likes表插入/删除时修改videos表对应视频likes_num字段

follows表:关注信息表
字段 说明
id 自增主键
following_user_id 外键(依赖于users.id),关注者用户id
followed_user_id 外键(依赖于users.id),被关注者用户id
relationship 好友标记,若为好友则为1,非好友则为0
联合唯一索引 following_user_id&followed_user_id

触发器:当follows表插入/删除时修改users表对应用户fans与follows字段

chats表:聊天记录表
字段 说明
id 自增主键
send_user_id 外键(依赖于users.id),发送信息者用户id
receive_user_id 外键(依赖于users.id),接受信息者用户id
timestamp 信息发送时间戳
content 信息内容(内容小于1000字符)

由于后期字段变更进行的数据库修改

user表:新增三个字段

用户头像url、顶部大图url、个人简介(因涉及到的内容均无接口可进行更改都设定了初始值)

基础接口

/douyin/feed/ - 视频流接口(唐振宇)

根据可选参数latest_time按时间返回指定数量的视频数据,若没有请求中没有latest_time,则将当前时间作为latest_time,latest_time的值应为每次返回视频列表最后一项的发布时间。当请求中有token时,还应检查该用户是否点赞视频。

/douyin/user/register/ - 用户注册接口(魏海林)

用户注册时候,提供用户名和密码,然后对两个参数合法性的检验(用户是否存在,长度,是否为空),默认名称为用户名,然后对用户密码进行加盐加密,最后对用户进行创建,然后使用JWT中间件返回token,最后返回用户ID和Token以及响应码。

/douyin/user/login/ - 用户登录接口(魏海林)

用户登录时,提供用户名和密码,然后对这两个参数进行合法性检验(长度,是否为空,用户是否存在),然后对密码进行加盐加密后匹配,最后返回用户ID和Token以及响应码。

/douyin/user/ - 用户信息(魏海林)

调用这个接口的时候,传入Token和当前用户ID参数,首先会校验Token,然后对用户ID进行校验(用户是否存在,ID是否合法),然后通过查询当前用户与传入用户ID的关系,判断二者的关系,最终返回响应。

/douyin/publish/action/ - 视频投稿(唐振宇)

**注意:**此接口的token是由form-data携带,本项目jwt中间件默认token由query参数携带,所以要对jwt中间件获取token方式做兼容处理。 视频数据作为文件直接存在服务器中,由nginx反向代理为外部提供访问。使用ffmpeg为视频截图制作封面,图片的格式选择jpg比png更好,因为大小更小,截图速度快9倍左右。将jwt中间件解析出的user_id作为作者id,由controller层从上下文获取。

/douyin/publish/list/ - 发布列表(唐振宇)

首先根据token中解析出的user_id获取该用户的视频列表,再遍历视频列表,获取每个视频的作者信息,和点赞信息。其中获取作者信息的同时,还要判断是否关注。(个人认为这个响应数据的设计不太合理,既然是一个用户的视频列表,那么作者信息查一次就可以了,没有必要video_list中每一项都去查作者信息

互动接口

/douyin/favorite/action/ - 赞操作(郑浩天)

注意:登录用户可以对视频点赞 执行赞操作之前首先通过中间件校验token,校验成功后通过GetInt64方法获取user_id,再根据action_type的值对指定的video_id进行点赞或者取消赞操作

  • 赞操作:首先判断赞是否存在,存在则返回错误;不存在则执行点赞操作,只需在likes表中插入一条新信息即可
  • 取消赞操作:首先判断赞是否存在,不存在则返回错误;存在则执行取消赞操作,因为前面已经判断赞是否存在,所以只需在likes表中删除该条信息

/douyin/favorite/list/ - 喜欢列表(郑浩天)

注意:is_follow字段表示是否关注,均指token所解析出的user_id对传递的user_id;且未登录的不能查看喜欢列表 首先使用中间件将token解析为user_id,然后根据传递的user_id通过gorm查询喜欢列表,并用JSON格式返回数据

/douyin/comment/action/ - 评论操作(郑浩天)

注意:登陆的用户才能对视频进行评论 执行评论操作之前首先通过中间件校验token,校验成功后通过GetInt64方法获取user_id,再根据action_type的值对指定的video_id进行发布评论和删除评论操作

  • 发布评论(返回comment_text)首先在comments表上添加一条评论信息,评论需要user_id、video_id、timestamp、content字段,插入成功后,返回comment_id给logic层,再查询评论用户信息,最后返回数据给controller层
  • 删除评论(返回commen_id)直接根据comment_id删除评论信息,删除出错则返回错误信息,无需返回评论信息

/douyin/comment/list/ - 视频评论列表(郑浩天)

注意:is_follow字段表示是否关注,均指token所解析出的user_id对传递的视频作者;且未登陆的用户可以查看评论列表

  • 登陆的用户:首先使用中间件将token解析为user_id,然后根据传递的video_id通过gorm查询视频评论列表,用JSON格式返回数据
  • 未登陆的用户:直接根据传递的video_id通过gorm查询视频评论列表,用JSON格式返回数据

社交接口

/douyin/relation/action/ - 关注操作(凌鑫杰)

执行操作之前,先通过中间件校验Token,然后对用户ID进行校验,再根据token中解析出的user_id与参数to_user_id进行后续操作(根据action_type值执行关注操作或取消关注操作)。

  • 关注操作:用户执行关注操作时,先检查关注对象是否为自己,是则返回错误;执行关注,先查询是否有对方关注信息存在,若存在修改标记并插入一条新信息(以上两个操作作为一个事务);否则只插入一条新信息
  • 取消关注:执行取消关注,先查询是否有对方关注信息存在,若存在修改标记并删除自己的关注信息(以上两个操作作为一个事务);否则只删除自己的关注信息

/douyin/relatioin/follow/list/ - 用户关注列表(凌鑫杰)

首先通过中间件校验Token,根据user_id参数值,通过数据库连接查询关注信息列表并格式化转换成JSON格式并返回数据。

/douyin/relation/follower/list/ - 用户粉丝列表(凌鑫杰)

首先通过中间件校验Token,根据user_id参数值,通过数据库连接查询得到粉丝信息列表并格式化转换成JSON格式并返回数据。

/douyin/relation/friend/list/ - 用户好友列表(凌鑫杰)

首先通过中间件校验Token,根据user_id参数值,通过数据库连接查询得到好友(即用户双方互相关注)信息列表并格式化转换成JSON格式并返回数据。 但是此处需要添加一个字段:用户头像url,因为用户头像在APP中无法更改,我们组使用的是静态图片,详见:【抖音大项目中一些坑

/douyin/message/chat/ - 聊天记录(郑浩天)

首先查看聊天记录之前,需要将token通过中间件解析为user_id,然后根据传递的to_user_id和pre_msg_time字段的值,通过gorm查询在pre_msg_time之后的新消息信息,因为要进行轮询访问,所以只需要新的消息,获取数据之后转化为JSON格式并返回数据 开发中遇到了一些问题,主要是测试的时候发现对接不上,最后发现APP相关的API接口出现了一些问题,导致这个接口开发不是很顺畅,详见:【抖音大项目中一些坑】。

/douyin/message/action/ - 发送消息操作(郑浩天)

执行发送操作之前首先通过中间件校验token,校验成功后通过GetInt64方法获取user_id,再根据action_type的值对指定的video_id进行发送消息操作,因为只开放了发送消息,所以只需要根据传递的to_user_id的值和content的值,将token解析的用户编辑的消息发送给传递的to_user_id,并返回操作成功的数据

四、Demo 演示视频

https://www.bilibili.com/video/BV1dg4y1p7L1/?vd_source=d8303f53ec80efe39094f58fdcd2db40

五、项目总结与反思

1.目前仍存在的问题

  • 因为大部分成员无Go语言基础与开发经验,对相关知识不太了解,项目没有进行很好的优化
  • 只使用了MySQL数据库,没有进一步在持久层加入使用缓存技术(如Redis),需要进一步优化
  • 项目APP前端关于用户聊天记录轮询未基于id去重,会造成重复轮询
  • 好友列表中用户头像无法更改,头像图片文件使用的是静态图片与静态url

2.已识别出的优化项

  • 持久层缓存
    • 在查询业务添加redis缓存,缓解mysql数据库压力
    • 缓存策略:只删除,不更新。一旦DB数据出现修改,就删除对应的缓存,而不去更新。
    • 缓存内容:只缓存完整的行数据,不缓存行的部分数据,因为这样数据更新时无法定位哪些数据要删除
    • 缓存处理方式:基于主键缓存
  • for循环中协程处理业务 for循环中的业务处理开启协程处理

3.架构演进的可能性

架构设计可以向微服务分布式架构方向发展,将分层结构更加细化,把每个功能都改变成微服务,在当前结构当中,如果某层出现问题,可能使这一层或者其他接口瘫痪,这样影响还是很大,把各个功能微服务化后,每个服务提供给需要的功能,如果一旦出现问题爆炸半径小,解耦程度高,独立扩展性更高,更具有通用性。

4.项目过程中的反思与总结

虽然项目的完成度很高,但是还是使用的单体架构,代码之间的业务很复杂,对一个功能进行拓展的时候至少会影响两个以上的功能,此外代码存在冗余,耦合、重构困难等情况,不过由于我们对golang基本都是0基础,所以很多东西只是设想去实现,但由于时间原因和熟练度原因就并没有去实现,比如使用redis非关系型数据库对点赞操作进行存储解决高并发的问题,使用消息队列对评论列表进行削峰处理等,但是我们都在尽全力的优化这个项目,让这个项目在我们力所能及的范围内达到最好。