project
课程预览
- 前端通过通过点击课程的预览按钮,进入预览页面。前端传递课程id,访问内容管理服务的CoursePublishController,根据课程id,将课程的师资信息,营销信息、基本信息、课程计划信息查询并组装。
- 组装后的信息通过freemarker模版引擎将数据渲染到course_template.ftl上,然后返回给前端。
- 进入预览页面,查看视频,会发送两次请求,先根据课程id到内容管理服务的CourseOpenController查询课程信息,这里调用的查询方法和第一步一样(没必要差这么全吧)。第二次将课程计划绑定的媒资id发送媒资管理服务的MediaOpenController,查询媒资的访问url,返回前端,以便播放。
课程提交审核
为实现审核过程中可以修改,设计了课程预发布表,机构人员可以随意修改课程计划、课程基本信息等。提交时,将这些信息组合起来存入预发布表。审核人员审核预发布表中的内容,审核中时,要把预发布表中的审核状态设置为审核中。
点击提交审核课程,传递课程id,访问CoursePublishController的方法,将课程的各种信息组装起来存入课程预发布表。存入课程预发布表时要先判断课程信息是否完备,完备后,查询预发布表中信息,看是否存在了,若存在,要看课程的审核状态,如果是审核中,则不可再提交,否则,对存在的信息进行更新操作,不存在就直接插入。然后修改课程基本信息表中的状态为审核中
由于进行了预发布表的插入和课程基本信息表的修改,要加上事务控制。
课程有两个状态一个审核状态{添加课程成功未提交审核 状态为“已提交”,提交审核了 “已提交”,审核通过了 “审核通过”,审核不通过 “审核不通过”} 一个发布状态{目前不清楚} 课程预发布表中有一个审核状态 {提交审核存进来的,初始都为 “已提交”,其他类似,只是没有未提交这个状态}
课程发布
分布式事务
分布式系统把一个应用系统拆分成分多个可独立部署的微服务,不同微服务间通过网络远程协作(调用)完成的事务操作就是分布式事务。
注
我之前一直以为只要是连的同一个主机的mysql,@Transaction就能控制事务。只要操作了不同的数据库(这个是指mysql中创建的database,不是指mysql本身,mysql属于数据库管理系统层面)就是分布式事务。但注意,如果是两个微服务连接的同一个数据库,也属于分布式事务,原因就是跨JVM进程,两个微服务持有了不同的数据库链接进行数据库操作,此时产生分布式事务。
分析
课程审核成功后,需要将预发布表中的信息复制到发布表。并把预发布表信息删掉
课程发布成功后,是可以被搜索到的,并且会有大量用户搜索。因此要把发布成功的信息存到 redis、es、minio。其中redis和es都是存课程信息,minio里面存渲染后的课程静态页面。
保存已发布课程的信息涉及到多个操作,涉及到分布式事务,用transaction是不行的。
解决方案
首先要考虑是AP还是CP(CAP理论),此处只说保证AP也就是最终一致性的解决方法。
使用XXL-Job+数据库表方案,当把发布的课程存入发布表后,再将课程存入消息表,(消息表与课程表在一个数据库中)此处是可以使用注解保证课程发布成功,消息表一定有数据。然后开启定时任务调度,将消息表中的数据取出依次完成存入redis、es、minio等操作,就算远程调用过程中出现问题,由于是定时调度,并且消息表中的数据还在,故可以保证最终一致性。
消息SDK组件内容补充
消息组件提供一套解决对消息的增删改查业务的方案,就是向外提供接口,但是不没有具体执行逻辑
消息表中主要就是保存业务的主要信息。如该项目中保存的是课程的id,通过拿到消息表中的课程id,就能查询到课程信息,然后进行缓存操作。消息表中的业务字段有多个,用不上的给该字段存null就行。消息表中还有多个状态字段。因为要存到redis、es、minio,每个阶段完成后给对应字段标记,可以减少重复执行。
补充关于XXL-Job使用分布式锁防止重复执行方案
xxl-job调度进行视频转码时,用了乐观锁的方案进行避免重复执行,因为视频转码比较消耗CPU,因此应该尽量避免重复执行,而对于缓存操作,不是特别消耗CPU(视频中老师说的)
使用xxl-job时,不是配置了调度策略嘛,为什么还要用乐观锁?
老师是这样解释的,当执行器与调度器之间的连接发生了中断,xxl-job就检测不到这个执行器了,虽然是发生了中断,但是执行器可能在执行任务了(执行任务时,连接中断了)。由于xxl-job是动态扩容,因此会对其他执行器进行重新排序(代码每个执行器就是根据自己的序号进行对应任务的处理),那么就导致其他执行器会调度执行(在断开连接的执行器执行完任务,删除任务表中数据之前)任务表中的任务,就有可能重复执行。
页面静态化
对于课程预览页面,用户搜索到就能查看,如果每次都访问tomcat服务器查询数据渲染返回,会使服务器压力过大,因此需要对课程预览页进行静态化并存储到minio。
因为课程页面的静态化属于内容管理服务,而静态页面上传到minio属于文件服务,生成的静态页面在内存中,并没有在数据库存,不能通过发两次请求来实现。因此要进行远程调用。
内容管理服务调用媒资服务,那么就要在内容管理服务编写Feign接口,编写规则就是将媒资相关controller接口方法copy过来,写到feign接口中,接口要通过注解指定媒资服务的名称,启动类要加上开启远程调用注解,
熔断降级
熔断降级的相同点都是为了解决微服务系统崩溃的问题,但它们是两个不同的技术手段,两者又存在联系。
熔断:
当下游服务异常而断开与上游服务的交互,它就相当于保险丝,下游服务异常触发了熔断,从而保证上游服务不受影响。
降级:
当下游服务异常触发熔断后,上游服务就不再去调用异常的微服务而是执行了降级处理逻辑,这个降级处理逻辑可以是本地一个单独的方法。
存入索引库(ES)
存入索引库这不操作,可以在课程发布成功,插入到发布表的地方通过编写代码实现,但这样会让业务耦合。实现数据库与索引库同步有很多办法,包括使用 消息队列mq,XXL-Job定时调度、还有阿里Canal,它是通过解析mysql的一个日志文件,来判断数据库进行了哪些的操作,然后将这写操作同步到索引库。因为该项目从课程发布那一节之后,就选择的是用消息sdk来实现,也就是存入es、redis、上传minio都是从数据的消息表里面读,然后进行相应同步。
回顾
之前的视频转码是用的是xxl-job,但没有用sdk组件,媒资数据库有个任务表,是从那个任务表里面拿到任务,然后进行转码处理。而在媒资之后,用到xxl-job的地方就是课程发布后,缓存的同步,有静态页面的上传,es的同步,redis的同步。