1. 项目介绍

​ 本项目是一个在线教育系统,网上教学平台,类似于智慧树,适用于学院的线上教学,老师在上面发布网课,同学们可以观看等等。

2. 系统架构

​ 系统以微服务为技术架构,大致分为三大服务:系统服务,用户服务,内容服务。系统以Spring cloud Gateway作为网关,以nacos作为注册中心与配置中心,用Redis存储系统缓存与Token,Mysql作为数据库等等。

  • 系统服务:主要有词典与鉴权功能。

    • 词典存储着一些String信息,如图

      系统词典表

      鉴权实现了用户校验与单点登录等功能。

  • 用户服务:主要有学生服务与教师服务

  • 内容服务:主要分为课程服务与媒资服务

    • 课程服务:课程信息的crud等等
      • 针对课程发布信息实现了搜索功能,集成了ElasticSearch。
    • 媒资服务:管理课程关联的视频大文件。通过本地搭建的MinIO分布式文件系统(分了五个子文件夹,当作分布式),实现了视频大文件的断点续传与合并。

3. 项目难点

1. 缓存一致性

背景:在查询课程内容时,最开始采用的是直接查询数据库,但是发现不太好,所以加了一层缓存。这时候就存在一个缓存一致性问题。然后就有两种方案:1. 先更新数据库,再删除缓存;2. 先删除缓存,再更新数据库

先说第一种方案,如果先更新数据库,可能在更新的时候,数据库或者服务宕机了,缓存没有被删除,这样会导致之前的旧数据还存在Redis里,造成脏数据,故而采用方案二。但是方案二也有问题,当有一个更新请求进来时,已经执行完删除缓存了,这时候来了一个查询的请求,查到缓存不存在,然后将Mysql里面还没更新的数据进行了缓存,这时候更新请求再接着继续,这样也会导致缓存了脏数据。

鉴于这种情况,我在网上查找了方案,发现可以采用延时双删的策略,就是先删除缓存,再更新数据库,然后业务休眠一段时间(我们根据业务实际情况,采用的是更新完数据库后,休眠500ms,使用Thread.sleep()),但是目前这种方案也有缺点,不能说每次更新的请求,都要用个500ms以上。

所以后续优化的话,我们在网上找到了几种方案,MQ和canal

  1. MQ

    采用消息队列,当更新完数据库的时候,生产者发送消息到消息队列,Redis部分监听消息队列再进行删除操作。

    但是这种方案会增加业务的复杂性,需要保证MQ的可靠性(生产者可靠性、消息队列持久化和稳定性、消费者可靠性、消息的幂等性等等),还需要再在业务代码里添加相应的生产者、消息队列、消费者等。

  2. canal

    cannal是阿里开发的中间件,把自己伪装成MySQL slave,模拟MySQL slave的交互协议向MySQL Mater发送 dump协议,MySQL mater收到canal发送过来的dump请求,开始推送binary log给canal,然后canal解析binary log,再发送到存储目的地,比如MySQL,Kafka,Elastic Search等等。

2. Spring事务失效

背景:在上传、合并媒资文件时,发现上传时比较慢,因为时本地搭建的MinIO(不会出现慢或者宕机的情况),然后就去找代码原因,发现原先的做法是将上传与入库放在一整个事务中,而且入库部分代码重复了(上传与合并都需要将信息入库),所以就将入库部分代码提取出来,且因为上传功能实现了断点续传,所以上传部分不需要开启事务,只需要入库时开启事务。但后面进行测试的时候(分为三点,手动抛异常),出现了入库事务失效。后面上网搜、问chat,发现是Spring事务失效。

原因:调用自身方法时,采用的不是代理对象,导致事务失效。

原理:Spring的事务是基于AOP实现的,原理就是动态代理,在代理对象的外部进行增强等操作。