抖声项目汇总
本文最后更新于:4 months ago
抖声项目汇总
单体架构仓库:https://github.com/Wuhlan3/dousheng
微服务架构仓库:https://github.com/Wuhlan3/kitexdousheng
一、简单介绍一下你的项目?
本项目的一些素材来源于字节跳动后端训练营,他们提供了一个抖声客户端,并且规范了可以实现的一些接口,包括用户登陆与注册、视频流、评论功能、点赞功能、关注功能等等。在训练营期间,我曾担任小组的队长,由于同小组有较多大一大二的同学,所以负责了该项目单体架构的设计、数据库表的设计、大部分接口的功能实现。最终该项目取得了较好的成绩。之后,我和舍友也尝试使用在青训营所学的知识,使用字节跳动的微服务架构KiteX框架来重新实现该项目,并且完善缓存机制、单元测试,完善了视频流的一些功能包括使用使用FFmpeg截取视频的封面、将视频通过存储桶来进行管理等等。
二、你主要做了哪些工作呢?
- 项目的整体设计(这主要是参考了一下KiteX框架下提供的一个笔记服务的样例,参照其文件的架构设计的)
- 数据库的设计和ER图的绘制也是我做的;
- 较完善的参数校验,对密码进行加密,使用JWT鉴权;
- 视频流的相关实现计包括将视频和封面上传到腾讯云COS对象存储桶,使用FFmpeg截取视频的封面;
- 使用Redis来减少访问数据库的次数
三、本项目你遇到的最大困难是什么,如何解决的?
我认为本项目最复杂的、也是最有必要认真实现的模块就是视频流的设计。
- 视频封面的获取,ffmpeg;
- 视频的存储方式——上传至腾讯云对象存储,并将对应的URL存放到数据库中(尝试过直接存储云服务器的本地文件夹中,但是由于带宽问题,且管理起来比较混乱,导致效果非常差)
- Redis的设计,涉及到两种数据结构ZSET和HASH,其中ZSET保存了视频的id和发布的时间latestTime(作为score)。在获取视频流的时候,我们首先通过获取最近发布的30个视频,之后根据视频ID再从Redis中获取视频相应的信息;优化之后,通过压力测试可以发现,报文返回的速度从100ms提升到50ms。
- 但是发现真正的卡顿问题出现在视频文件的传输上,考虑到我的云服务器是比较小型的服务器,2核2G的,带宽也比较有限,所以我尝试将视频上传到云服务器上。
- 同时使用了ffmpeg对视频进行处理,包括格式的转换,视频封面的获取等等。
- 最终使得客户端程序在运行的情况体验比较正常
四、数据库表是如何设计的?
主要涉及六张表,分别是user、relation、video、favorite、comment和publish。
其中,我认为最重要的两个实体是用户和视频,其他的表可以看作是这两者之间的关系。
用户表包括用户ID、账号、密码、关注数、被关注数等信息;
video表包括ID、视频封面的URL、视频URL、视频名称、上传者的ID、点赞数等信息;
…
此外,还对外键依赖、非空、唯一性等约束进行了设计。为了提高查询效率,适当的生成了索引。比如说,视频的上传者ID,点赞表中的 user_id和video_id作为联合索引。
六、整体架构是怎么设计的?
使用了Gin框架来开放HTTP端口,通过封装好的RPC客户端与微服务中的服务端进行通信。
RPC服务端通过接收客户端的请求,在各自的进程中完成业务逻辑和数据库的交互。
数据库层面使用了Redis缓存机制,使用MySQL进行持久化。
序列化手段一般包括JSON、ProtoBuf、thrift等。抖声客户端与服务器的交互直接采用的JSON,而RPC通信过程中使用了ProtoBuf来进行序列化。
JSON,ProtoBuf,thrift之间的区别是什么呢?
ProtoBuf无论是序列化和反序列化的速度,还是编码后的数据大小,都比JSON优秀很多,但是其可见性较差。因此在微服务之间通信时,protobuf 更合适,在公开 API 或与浏览器通信时JSON 与 xml 更合适。
protobuf与thrift的对比:
protobuf thrift 功能特性 主要是一种序列化机制 提供了全套RPC解决方案,包括序列化机制、传输层、并发处理框架等 支持语言 C++/Java/Python C++, Java, Python, Ruby, Perl, PHP, C#, Erlang, Haskell 易用性 语法类似,使用方式等类似 生成代码的质量 可读性都还过得去,执行效率另测 升级时版本兼容性 均支持向后兼容和向前兼容 学习成本 功能单一,容易学习 功能丰富、学习成本高 文档&社区 官方文档较为丰富,google搜索protocol buffer有2000W+结果,google group被墙不能访问 官方文档较少,没有API文档,google搜索apache thrift仅40W结果,邮件列表不怎么活跃
七、微服务架构相比于单体架构,优点体现在哪里?
微服务架构的优点如下:
- 单一职责。每个服务都有自己独立的业务逻辑,是一个高内聚、低耦合、单一原则的单元。
- 轻量级通信。服务之间用过轻量级的通信机制实现互联互通,通常是语言无关、平台无关的交互方式;
- 独立性。每个服务可以独立地进行开发、测试和部署;
- 进程隔离。每个服务高度自治,可以部署到不同的主机上。当其中一个服务挂掉时,不会影响其他服务。
使用微服务的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现公司跨团队的技术解耦。但是这样子,如果没有一个统一的服务框架——RPC框架,则各个团队需要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理等业务之外的重复技术劳动。统一RPC框架将上述的劳动进行了统一处理。
八、ETCD是什么?
ETCD是Go语言开发的一个开源的、高可用的分布式key-value存储系统。
微服务在ETCD注册中心中注册好自己的相关信息,之后api层可以通过ETCD来获取相应微服务的ip地址和端口号,从而发起RPC调用。
九、有没有对该项目的性能进行测试?
由于环境的限制,在两核2G的小型云服务器环境下,在30秒内5000个客户端的情况下,可以处理40000个请求。之后还会继续完善测试。
十、JWT相关原理
是作为中间件来使用的,大部分的HTTP接口都需要JWT校验通过之后,才能够进行下一步的业务逻辑处理。
JWT的实现原理:
- 用户登录的时候,会将账号和密码发送给服务器;
- 服务器验证账号密码的有效性后,会返回签名后的token给客户端;
- 客户端将token存储在本地存储中
- 之后客户端的请求,都应该携带上token,服务端在对token解码之后,判断其解码的有效性,从而认证客户的身份。
签名的过程:
首先JWT的结构如下:
- 头部:包含了基本信息;基于Base64编码规则
- 荷载:包含了签发者、用户、接收方、过期时间等等;基于Base64编码规则
- 签名:使用HS256算法来对(头部和荷载)进行加密。(作用是防止篡改)
十一、RPC的原理
十二、Gin有什么特点?
- 路由
- context上下文,方便传递
十三、你的项目中使用到了什么设计模式?
-
在repository层使用了单例模式。比如说我们给UserDao绑定了Query、Update等方法,在每次调用的时候,我们都返回userDao这个单例对象,而避免重复生成,消耗额外的资源。(对于每个线程,可共享一个实例)
-
在service层,我们使用了简单工厂模式,比如说对于userlogin这个模块,我们定义了一个UserLoginFlow的对象,每次调用它的方法,我们首先要根据所需的参数new出这一个对象,之后做参数校验、逻辑处理等工作。简单工厂模式可以确保我们创建的实例具有需要的参数,进而保证实例的方法可以按预期执行
-
工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;
-
简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
-
客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
-
通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
-
十四、代码行数
//算上kitexgen自动生生成的有9218行
find . "(" -name "*.go" ")" -print | xargs wc -l
//如果不算,有3702行
find cmd config pkg "(" -name "*.go" ")" -print | xargs wc -l
十五、怎么测试抖音
基本思路:
- 回答功能层面的测试点(最重要的)
- 之后拓展到 UI测试、兼容性测试、易用性测试、可移植性测试、性能测试等等。
功能测试的中的概念就在于寻找输入口:
- 上拉测试,视频是否加载、用户名称、title、点赞数、收藏数是否符合预期
- 下拉测试;
- 多次上拉,查看是否不同的视频
- 多次上拉,查看视频是否按照规定的顺序切换:(比如我的项目中采用的是上传时间)
- 点赞,查看点赞数是否增加;在关闭程序后,再次查看该视频,看点赞数是否持久化。
- 关注,测试过程与点赞类似。但是除了查看我的关注数是否增加外,还需要查看对方的页面,被关注数是否增加。同时还要查看关注和被关注列表
- 关闭App后,视频是否会重复推荐,是否影响正常的体验。
UI测试:UI是否维持它原有的状态;界面是否美观、颜色搭配是否合理、布局是否合理、按钮大小位置是否合适、文字大小颜色搭配是否合适。
兼容性测试:在不同的手机型号、不同的分辨率下、不同的操作系统,测试效果如何。
易用性测试:是否由明显的提示,用户是否可以在点击三次鼠标内就达到目的;评论功能的复制粘贴。
性能测试:视频加载的速度。视频上传的速度。
安全性测试:登录验证码,记住密码,自动登录,找回密码,SQL注入问题,报文是否加密
中断测试:退出后再进来
网络测试:测试下在弱网,无网,4G,5G网络下面,这个功能是否还正常,有对应处理,比如无网就要提示等等。(有用过一些工具比如fiddler去模拟弱网等场景。)
十六、项目中有没有使用到事务?
有。Gorm中自动开启
十七、Docker有什么特点?
Docker包括三个基本概念:
- docker镜像:就相当于是一个 root 文件系统。
- docker容器:镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
- docker仓库:仓库可看成一个代码控制中心,用来保存镜像。
docker的三大底座:
-
namespace 命名空间:容器隔离的基础,保证A容器看不到B容器.
-
cgroups 控制组:主要用到的cgroups子系统:cpu,blkio,device,freezer,memory
实际上 Docker 是使用了很多 Linux 的隔离功能,让容器看起来像一个轻量级虚拟机在独立运行,容器的本质是被限制了的 Namespaces,cgroup,具有逻辑上独立文件系统,网络的一个进程。
-
unionfs 联合文件系统:是一种轻量级的高性能分层文件系统,支持将文件系统中的修改进行提交和层层叠加,这个特性使得镜像可以通过分层实现和继承。
十八、Docker和虚拟机的对比
- 虚拟机需要模拟整个机器包括硬件、库、一个完整的用户操作系统。而Docker是与宿主机共享硬件资源及操作系统,可以实现资源的动态分配。
- 虚拟机的启动在分钟级别、docker的启动在秒级别可以做到快速部署
- docker需要的资源更少,在操作系统级别进行虚拟化,容器与内核的交互几乎没有额外的损耗
- docker的隔离性更弱、安全性也更弱。属于进程之间的隔离。虚拟机可实现系统级别的隔离。docker与宿主机共享内核、文件系统等资源,更有可能对其他容器、宿主机造成影响。
十九、Dockerfile怎么写
二十、密码怎么加密的,使用了什么算法
使用的是bcrypt中的一个库函数。就是一个加盐的过程。先生成一个随机数salt,然后和password一起进行hash。
对同一个密码,每次生成的hash不一样,这是因为hash中包含了salt(hash产生过程:先随机生成salt,salt跟password进行hash);
在下次校验时,从hash中取出salt,salt跟password进行hash;得到的结果跟保存在DB中的hash进行比对,compareSync中已经实现了这一过程:bcrypt.compareSync(password, hashFromDB);
二十一、登陆方式
cookie和session的查缺补漏
JWT加密方式:HS256,是对称加密算法
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!