- 本项目采用微服务架构,目录结构如上所示
- service下面包括
- api层,暴露RESTful API
- rpc-user-info层是完成用户注册、登陆、查看用户信息,主要是对User表进行CURD
- rpc-user-operator层是完成点赞、评论、关注操作,
主要是对user_favorite_list、user_follow_list、user_comment_list表的CURD
- rpc-video-service层是完成视频的发布、拉取、视频流操作,主要是对Video表进行CURD
- mq层,是kafka中间件的消费者,因为我认为,点赞、关注、评论的实时性较强,而重要程度并没有那么高,所以先将前端的message,Push到kafka的Topic中再对数据库进行CURD
- tpl层是goctl生成时所需的template文件
- Common层 存放错误代码,cos上传下载等操作
- 所使用的框架以及技术栈如下
- go-zero 以及生成脚手架的工具goctl
- MySQL
- Redis
- Kafka
- ZooKeeper
- Docker
- Etcd
- // k8s(kind (k8s.io)单机下集群部署) ,没空折腾了
- 开发、运行环境
WSL:开发环境
Docker:部署微服务以及etcd,kafka
阿里云:部署MySQL以及Redis
设计数据库的步骤
- 了解需求
- 标识实体:具体存在的对象(名词)用户,视频,评论,赞
- 标识属性
- 标识关系
项目主要功能
- 用户:1. 可以进行登录以及注册,发布视频,
-
对视频点赞 \ 取消赞、评论 \ 删评论,
-
对其他用户加关注,
-
显示用户自己投稿的视频、自己的粉丝、自己关注的用户、自己点赞的视频列表
- 视频:可以查看评论列表、点赞数
需求分析
- 用户注册和登陆,后台数据需要存放用户的名称、密码、关注数、关注列表、粉丝数、粉丝列表、发布列表、以及点赞列表
- 视频,后台数据库需要存放视频的内容、封面的内容、发布者信息、点赞数、评论数、评论列表
- 评论,后台数据库存放评论的内容、评论人ID、被评论视频ID、
表结构
User
Video
用户的关注列表
这里更新带索引是想在返回的列表里按照关注时间进行返回
用户的点赞列表
视频的评论列表
关系
- 一对一:主键对主
- 一对多:主键对非主键
- 多对一:非主键对主键
- 多对多:非主键对非主键
一个用户对应多个视频
一个视频对应多个评论
三范式
- 所有字段不可再分 比如湖北省襄阳市 还可以再分为湖北、襄阳
- 首先得满足第一范式,非主键字段必须依赖于主键字段,比如一张表中有订单号 产品编号 产品价格 那么还可以将产品编号和产品价格独立出来
- 非主键字段中消除传递依赖,比如一张表中有订单号 顾客编号 顾客姓名,那么顾客编号和顾客姓名应该是一一对应的,就没必要再写顾客姓名可以再建一张表记录顾客姓名。即如果一个字段可以推导出另一个字段那么就有传递依赖
第二范式是不允许非主键只依赖主键的一部分,第三范式是非主键必须得直接依赖主键
手机app与wsl连接
关闭windows防火墙
进行端口映射
hyper-v启动后的保留端口范围
netsh interface ipv4 show excludedportrange protocol=tcp
以管理员身份打开 Powershell
- 将本地9999端口暴露给外网
netsh interface portproxy add v4tov4 listenaddress=* listenport=9999 connectaddress=wslnet connectport=8888
connectaddress:通过ip addr | grep eth0进行获取
- 查看所有暴露的端口
netsh interface portproxy show all
- 删除暴露的端口
netsh interface portproxy delete v4tov4 listenport=9999 listenaddress=*
后面老是觉得不方便换成了安卓虚拟器,但是很多虚拟器都不支持hyper-v.....
后来查了下换成了BlueStacks
项目记录(整理思路所留下来的记录)
注册、登陆、用户信息
注册
- 前端输入用户名、密码后,api直接传给rpc
- rpc对密码进行bcrypt加密后传入数据库,然后生成token放到redis之后返回给api
Token 只有1天的存活时长
登陆
- 前端输入用户名、密码后,api直接传给rpc
- rpc首先通过用户名查询用户,mysql返回用户的一行信息
- rpc对密码进行bcrypt加密对比数据库里的密码
- 再通过userId查询redis是否存在token
- 若没有则生成一个token放入redis并返回给api
用户信息
- 前端传入用户id和token
- 可以让go-zero自动鉴权但是!前端不是放到Header里而是form的形式很拉胯
- 然后就是对token进行解析,是否符合条件
- 这里的userId可能不是自己的id,可能是当前用户查看的id,我目前还没有管这些,所以在返回时is_follow字段我直接设置的true
投稿、发布列表
采用腾讯COS对象存储
设置工作流自动对上传的视频进行封面截图保存
并且用户上传的图片存在视频桶的以user_id命名的文件夹下,文件的key必须以“.mp4”为后缀,其次文件前缀通过雪花算法生成sony/sonyflake --- snowId
那么视频key就是snowId.mp4,文件类型是啥呢?avi,mp4?得判断文件的后缀名
生成的视频key是snowId.mp4,外链url:video-1312359504.cos.ap-guangzhou.myqcloud.com/user_id/sno…
然后生成的封面key是snowId_0.jpg,外链url:cover-1312359504.cos.ap-guangzhou.myqcloud.com/user_id/sno…
// 视频示例video-1312359504.cos.ap-guangzhou.myqcloud.com/1/411751041…
// 封面示例cover-1312359504.cos.ap-guangzhou.myqcloud.com/1/411751041…
前端输入文件、token、title
对点赞、评论、关注的解决方案的思考
点赞的特点:
- 高并发
- 实时响应
解决方案:高并发 go天生支持,实时响应那么就完成消息发往Kafka就直接返回
存在的问题:
- 高频的点赞请求应该这么办呢?
通过消息队列+Redis来抗住(太恶意的 开脚本刷点赞 这种直接限制该用户的点赞权限)
- 什么时候做数据的持久化操作?
通过消费者向Redis写的时候需要设置一个expire time(因为点赞数据并没有那么重要),那么就需要在这个expire time时间内将数据读走,我目前的知识面是启动一个定时任务进行处理,这个任务需要在expire time的时间内将数据写入数据库,我想的是 expire time = 5min,任务每分钟读取一次Redis(想的是最差情况下尽可能有两次可以保证数据的写入,但是这样可能有点问题,导致只能读到1次?)
以上是我对存在问题的解决方案
那么这个解决方案有什么问题呢?(这些都应该和集群有关系?)
- 如果存在多个kafka,就需要对Redis进行加锁的处理
- 同步Redis数据的时间的问题:
- 过短:MySQL访问的压力大
- 过长:如果Redis宕机,就可能造成数据的丢失
- 如何保证Redis中的数据不被多次消费(多次消费也没啥用,徒增了数据库的访问压力),我想的是使用一个标志位,记录数据是否被写入数据库。 0:未写入,1:已写入,每次写入Redis,
点赞应该保证的是最终一致性,那么状态无非是点赞状态,未点赞状态,那么第一次在前端进行点击就是1点赞了,下次来了就是未点赞--->奇数就是点赞状态--->偶数就是未点赞
评论,关注的话也是类似吧?
暂时无法在文档外展示此内容
暂时无法在文档外展示此内容
Asynq
asynq的shedule
分为client与server,client用来定义调度时间,server是到了时间接受client的消息触发来执行我们写的业务的,实际业务我们应该写在server,client用来定义业务调度时间的
性能优化
MapReduce
在feed流接口中需要拉取与视频作者的关注关系,与视频的点赞关系,那么如果串行执行时间消耗比较大,利用MapReduce并行执行就能大大减少时间的消耗,我在本地测试的结果是,串行时时间消耗在300ms,优化后在150ms,减少了一半的时间损耗。