项目实战 | 青训营笔记

1,444 阅读7分钟

  1. 本项目采用微服务架构,目录结构如上所示
  2. 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上传下载等操作
  1. 所使用的框架以及技术栈如下
  • go-zero 以及生成脚手架的工具goctl
  • MySQL
  • Redis
  • Kafka
  • ZooKeeper
  • Docker
  • Etcd
  • // k8s(kind (k8s.io)单机下集群部署) ,没空折腾了
  1. 开发、运行环境

WSL:开发环境

Docker:部署微服务以及etcd,kafka

阿里云:部署MySQL以及Redis

设计数据库的步骤

  1. 了解需求
  2. 标识实体:具体存在的对象(名词)用户,视频,评论,赞
  3. 标识属性
  4. 标识关系

项目主要功能

  • 用户:1. 可以进行登录以及注册,发布视频,
  1. 对视频点赞 \ 取消赞、评论 \ 删评论,

  2. 对其他用户加关注,

  3. 显示用户自己投稿的视频、自己的粉丝、自己关注的用户、自己点赞的视频列表

  • 视频:可以查看评论列表、点赞数

需求分析

  • 用户注册和登陆,后台数据需要存放用户的名称、密码、关注数、关注列表、粉丝数、粉丝列表、发布列表、以及点赞列表
  • 视频,后台数据库需要存放视频的内容、封面的内容、发布者信息、点赞数、评论数、评论列表
  • 评论,后台数据库存放评论的内容、评论人ID、被评论视频ID、

表结构

User

Video

用户的关注列表

这里更新带索引是想在返回的列表里按照关注时间进行返回

用户的点赞列表

视频的评论列表

关系

  • 一对一:主键对主
  • 一对多:主键对非主键
  • 多对一:非主键对主键
  • 多对多:非主键对非主键

一个用户对应多个视频

一个视频对应多个评论

三范式

  1. 所有字段不可再分 比如湖北省襄阳市 还可以再分为湖北、襄阳
  2. 首先得满足第一范式,非主键字段必须依赖于主键字段,比如一张表中有订单号 产品编号 产品价格 那么还可以将产品编号和产品价格独立出来
  3. 非主键字段中消除传递依赖,比如一张表中有订单号 顾客编号 顾客姓名,那么顾客编号和顾客姓名应该是一一对应的,就没必要再写顾客姓名可以再建一张表记录顾客姓名。即如果一个字段可以推导出另一个字段那么就有传递依赖

第二范式是不允许非主键只依赖主键的一部分,第三范式是非主键必须得直接依赖主键

手机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

项目记录(整理思路所留下来的记录)

注册、登陆、用户信息

注册

  1. 前端输入用户名、密码后,api直接传给rpc
  2. rpc对密码进行bcrypt加密后传入数据库,然后生成token放到redis之后返回给api

Token 只有1天的存活时长

登陆

  1. 前端输入用户名、密码后,api直接传给rpc
  2. rpc首先通过用户名查询用户,mysql返回用户的一行信息
  3. rpc对密码进行bcrypt加密对比数据库里的密码
  4. 再通过userId查询redis是否存在token
  5. 若没有则生成一个token放入redis并返回给api

用户信息

  1. 前端传入用户id和token
  2. 可以让go-zero自动鉴权但是!前端不是放到Header里而是form的形式很拉胯
  3. 然后就是对token进行解析,是否符合条件
  4. 这里的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就直接返回

存在的问题:

  1. 高频的点赞请求应该这么办呢?

通过消息队列+Redis来抗住(太恶意的 开脚本刷点赞 这种直接限制该用户的点赞权限)

  1. 什么时候做数据的持久化操作?

通过消费者向Redis写的时候需要设置一个expire time(因为点赞数据并没有那么重要),那么就需要在这个expire time时间内将数据读走,我目前的知识面是启动一个定时任务进行处理,这个任务需要在expire time的时间内将数据写入数据库,我想的是 expire time = 5min,任务每分钟读取一次Redis(想的是最差情况下尽可能有两次可以保证数据的写入,但是这样可能有点问题,导致只能读到1次?)

以上是我对存在问题的解决方案

那么这个解决方案有什么问题呢?(这些都应该和集群有关系?)

  1. 如果存在多个kafka,就需要对Redis进行加锁的处理
  2. 同步Redis数据的时间的问题:
  • 过短:MySQL访问的压力大
  • 过长:如果Redis宕机,就可能造成数据的丢失
  1. 如何保证Redis中的数据不被多次消费(多次消费也没啥用,徒增了数据库的访问压力),我想的是使用一个标志位,记录数据是否被写入数据库。 0:未写入,1:已写入,每次写入Redis,

点赞应该保证的是最终一致性,那么状态无非是点赞状态,未点赞状态,那么第一次在前端进行点击就是1点赞了,下次来了就是未点赞--->奇数就是点赞状态--->偶数就是未点赞

评论,关注的话也是类似吧?

暂时无法在文档外展示此内容

暂时无法在文档外展示此内容

Asynq

asynq的shedule

分为client与server,client用来定义调度时间,server是到了时间接受client的消息触发来执行我们写的业务的,实际业务我们应该写在server,client用来定义业务调度时间的

性能优化

MapReduce

在feed流接口中需要拉取与视频作者的关注关系,与视频的点赞关系,那么如果串行执行时间消耗比较大,利用MapReduce并行执行就能大大减少时间的消耗,我在本地测试的结果是,串行时时间消耗在300ms,优化后在150ms,减少了一半的时间损耗。