学成在线----day8

news2024/10/6 16:28:26

1、课程发布

为了提高网站的速度需要将课程信息进行缓存,并且要将课程信息加入索引库方便搜索,下图显示了课程发布后课程信息的流转情况:
在这里插入图片描述
1、向内容管理数据库的课程发布表存储课程发布信息,更新课程基本信息表中发布状态为已发布。
2、向Redis存储课程缓存信息。
3、向Elasticsearch存储课程索引信息。
4、请求分布文件系统存储课程静态化页面(即html页面),实现快速浏览课程详情页面。

课程发布表的数据来源于课程预发布表,它们的结构基本一样,只是课程发布表中的状态是课程发布状态,如下图:
在这里插入图片描述
redis中的课程缓存信息是将课程发布表中的数据转为json进行存储。
elasticsearch中的课程索引信息是根据搜索需要将课程名称、课程介绍等信息进行索引存储。
MinIO中存储了课程的静态化页面文件(html网页),查看课程详情是通过文件系统去浏览课程详情页面。

2、什么是分布式事务

一次课程发布操作需要向数据库、redis、elasticsearch、MinIO写四份数据,这里存在分布式事务问题。

什么是分布式事务?
首先理解什么是本地事务?

平常我们在程序中通过spring去控制事务是利用数据库本身的事务特性来实现的,因此叫数据库事务,由于应用主要靠关系数据库来控制事务,此数据库只属于该应用,所以基于本应用自己的关系型数据库的事务又被称为本地事务。

本地事务具有ACID四大特性,数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作 要么都成功,要么都失败,只要其中任一操作执行失败,都将导致整个事务的回滚。

理解了本地事务,什么是分布式事务?

现在的需求是课程发布操作后将数据写入数据库、redis、elasticsearch、MinIO四个地方,这四个地方已经不限制在一个数据库内,是由四个分散的服务去提供,与这四个服务去通信需要网络通信,而网络存在不可到达性,这种分布式系统环境下,通过与不同的服务进行网络通信去完成事务称之为分布式事务。

3、 什么是CAP理论

控制分布式事务首先需要理解CAP理论,什么是CAP理论?
CAP是 Consistency、Availability、Partition tolerance三个词语的缩写,分别表示一致性、可用性、分区容忍性。
使用下边的分布式系统结构 进行说明:
在这里插入图片描述
客户端经过网关访问用户服务的两个结点,一致性是指用户不管访问哪一个结点拿到的数据都是最新的,比如查询小明的信息,不能出现在数据没有改变的情况下两次查询结果不一样。

可用性是指任何时候查询用户信息都可以查询到结果,但不保证查询到最新的数据。
分区容忍性也叫分区容错性,当系统采用分布式架构时由于网络通信异常导致请求中断、消息丢失,但系统依然对外提供服务。

CAP理论要强调的是在分布式系统中这三点不可能全部满足,由于是分布式系统就要满足分区容忍性,因为服务之间难免出现网络异常,不能因为局部网络异常导致整个系统不可用。

满足P那么C和A不能同时满足:
比如我们添加一个用户小明的信息,该信息先添加到结点1中,再同步到结点2中,如下图:
在这里插入图片描述
如果要满足C一致性,必须等待小明的信息同步完成系统才可用(否则会出现请求到结点2时查询不到数据,违反了一致性),在信息同步过程中系统是不可用的,所以满足C的同时无法满足A。

如果要满足A可用性,要时刻保证系统可用就不用等待信息同步完成,此时系统的一致性无法满足。

所以在分布式系统中进行分布式事务控制,要么保证CP、要么保证AP。

4、什么是BASE理论?

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。
基本可用:当系统无法满足全部可用时保证核心服务可用即可,比如一个外卖系统,每到中午12点左右系统并发量很高,此时要保证下单流程涉及的服务可用,其它服务暂时不可用。
软状态:是指可以存在中间状态,比如:打印自己的社保统计情况,该操作不会立即出现结果,而是提示你打印中,请在XXX时间后查收。虽然出现了中间状态,但最终状态是正确的。
最终一致性:退款操作后没有及时到账,经过一定的时间后账户到账,舍弃强一致性,满足最终一致性。

5、分布式事务控制有哪些常用的技术方案?

实现CP就是要实现强一致性:
使用Seata框架基于AT模式实现
使用Seata框架基于TCC模式实现。
实现AP则要保证最终数据一致性:
使用消息队列通知的方式去实现,通知失败自动重试,达到最大失败次数需要人工处理;
使用任务调度的方案,启动任务调度将课程信息由数据库同步到elasticsearch、MinIO、redis中。

6、课程发布的事务控制方案

学习了这么多的理论,回到课程发布,执行课程发布操作后要向数据库、redis、elasticsearch、MinIO写四份数据,这个场景用哪种方案?

满足CP?
如果要满足CP就表示课程发布操作后向数据库、redis、elasticsearch、MinIO写四份数据,只要有一份写失败其它的全部回滚。
满足AP?
课程发布操作后,先更新数据库中的课程发布状态,更新后向redis、elasticsearch、MinIO写课程信息,只要在一定时间内最终向redis、elasticsearch、MinIO写数据成功即可。
目前我们已经有了任务调度的技术积累,这里选用任务调度的方案去实现分布式事务控制,课程发布满足AP即可。
下图是具体的技术方案:
在这里插入图片描述

1、在内容管理服务的数据库中添加一个消息表,消息表和课程发布表在同一个数据库。
2、点击课程发布通过本地事务向课程发布表写入课程发布信息,同时向消息表写课程发布的消息。通过数据库进行控制,只要课程发布表插入成功消息表也插入成功,消息表的数据就记录了某门课程发布的任务。
3、启动任务调度系统定时调度内容管理服务去定时扫描消息表的记录。
4、当扫描到课程发布的消息时即开始完成向redis、elasticsearch、MinIO同步数据的操作。
5、同步数据的任务完成后删除消息表记录。
时序图如下:
下图是课程发布操作的流程:
在这里插入图片描述
1、执行发布操作,内容管理服务存储课程发布表的同时向消息表添加一条“课程发布任务”。这里使用本地事务保证课程发布信息保存成功,同时消息表也保存成功。
2、任务调度服务定时调度内容管理服务扫描消息表,由于课程发布操作后向消息表插入一条课程发布任务,此时扫描到一条任务。
3、拿到任务开始执行任务,分别向redis、elasticsearch及文件系统存储数据。
4、任务完成后删除消息表记录。

7、课程发布接口开发

    @Transactional
    @Override
    public void publish(Long companyId, Long courseId) {

        //查询预发布表
        CoursePublishPre coursePublishPre = coursePublishPreMapper.selectById(courseId);
        if(coursePublishPre == null){
            XueChengPlusException.cast("课程没有审核记录,无法发布");
        }
        //状态
        String status = coursePublishPre.getStatus();
        //课程如果没有审核通过不允许发布
        if(!status.equals("202004")){
            XueChengPlusException.cast("课程没有审核通过不允许发布");
        }

        //向课程发布表写入数据
        CoursePublish coursePublish = new CoursePublish();
        BeanUtils.copyProperties(coursePublishPre,coursePublish);
        //先查询课程发布,如果有则更新,没有再添加
        CoursePublish coursePublishObj = coursePublishMapper.selectById(courseId);
        if(coursePublishObj == null){
            coursePublishMapper.insert(coursePublish);
        }else{
            coursePublishMapper.updateById(coursePublish);
        }

        //向消息表写入数据
//        mqMessageService.addMessage("course_publish",String.valueOf(courseId),null,null);
        saveCoursePublishMessage(courseId);

        //将预发布表数据删除
        coursePublishPreMapper.deleteById(courseId);

    }

8、课程发布消息sdk

课程发布操作执行后需要扫描消息表的记录,有关消息表处理的有哪些?
在这里插入图片描述
上图中红色框内的都是与消息处理相关的操作:
1、新增消息表
2、扫描消息表。
3、更新消息表。
4、删除消息表。
使用消息表这种方式实现最终事务一致性的地方除了课程发布还有其它业务场景。
在这里插入图片描述
如果在每个地方都实现一套针对消息表定时扫描、处理的逻辑基本上都是重复的,软件的可复用性太低,成本太高。
如何解决这个问题
针对这个问题可以想到将消息处理相关的逻辑做成一个通用的东西。
是做成通用的服务,还是做成通用的代码组件呢?
通用的服务是完成一个通用的独立功能,并提供独立的网络接口,比如:项目中的文件系统服务,提供文件的分布式存储服务。
代码组件也是完成一个通用的独立功能,通常会提供API的方式供外部系统使用,比如:fastjson、Apache commons工具包等。
如果将消息处理做成一个通用的服务,该服务需要连接多个数据库,因为它要扫描微服务数据库下的消息表,并且要提供与微服务通信的网络接口,单就针对当前需求而言开发成本有点高。
如果将消息处理做一个SDK工具包相比通用服务不仅可以解决将消息处理通用化的需求,还可以降低成本。
所以,本项目确定将对消息表相关的处理做成一个SDK组件供各微服务使用,如下图所示:
在这里插入图片描述
下边对消息SDK的设计内容进行说明:
sdk需要提供执行任务的逻辑吗
拿课程发布任务举例,执行课程发布任务是要向redis、索引库等同步数据,其它任务的执行逻辑是不同的,所以执行任务在sdk中不用实现任务逻辑,只需要提供一个抽象方法由具体的执行任务方去实现。
如何保证任务的幂等性
在视频处理章节介绍的视频处理的幂等性方案,这里可以采用类似方案,任务执行完成后会从消息表删除,如果消息的状态是完成或不存在消息表中则不用执行。
如何保证任务不重复执行
采用和视频处理章节一致方案,除了保证任务的幂等性外,任务调度采用分片广播,根据分片参数去获取任务,另外阻塞调度策略为丢弃任务。
注意:这里是信息同步类任务,即使任务重复执行也没有关系,不再使用抢占任务的方式保证任务不重复执行。

还有一个问题,根据消息表记录是否存在或消息表中的任务状态去保证任务的幂等性,如果一个任务有好几个小任务,比如:课程发布任务需要执行三个同步操作:存储课程到redis、存储课程到索引库,存储课程页面到文件系统。如果其中一个小任务已经完成也不应该去重复执行。这里该如何设计?

将小任务作为任务的不同的阶段,在消息表中设计阶段状态。
在这里插入图片描述

9、课程发布任务调度

package com.xuecheng.content.service.jobhandler;

import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.messagesdk.model.po.MqMessage;
import com.xuecheng.messagesdk.service.MessageProcessAbstract;
import com.xuecheng.messagesdk.service.MqMessageService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;


@Slf4j
@Component
public class CoursePublishTask extends MessageProcessAbstract {

    //任务调度入口
    @XxlJob("CoursePublishJobHandler")
    public void coursePublishJobHandler() throws Exception {

        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();//执行器的序号,从0开始
        int shardTotal = XxlJobHelper.getShardTotal();//执行器总数
        //调用抽象类的方法执行任务
        process(shardIndex,shardTotal, "course_publish",30,60);


    }

    //执行课程发布任务的逻辑,如果此方法抛出异常说明任务执行失败
    @Override
    public boolean execute(MqMessage mqMessage) {
        //从mqMessage拿到课程id
        Long courseId = Long.parseLong(mqMessage.getBusinessKey1());

        //课程静态化上传到minio
        generateCourseHtml(mqMessage,courseId);


        //向elasticsearch写索引数据
        saveCourseIndex(mqMessage,courseId);

        //向redis写缓存

        //返回true表示任务完成
        return true;
    }
    //生成课程静态化页面并上传至文件系统
    private void generateCourseHtml(MqMessage mqMessage,long courseId){
        //消息id
        Long taskId = mqMessage.getId();
        MqMessageService mqMessageService = this.getMqMessageService();

        //做任务幂等性处理
        //查询数据库取出该阶段执行状态
        int stageOne = mqMessageService.getStageOne(taskId);
        if(stageOne>0){
           log.debug("课程静态化任务完成,无需处理...");
           return ;
        }

        //开始进行课程静态化
        int i=1/0;//制造一个异常表示任务执行中有问题

        //..任务处理完成写任务状态为完成
        mqMessageService.completedStageOne(taskId);


    }

    //保存课程索引信息 第二个阶段任务
    private void saveCourseIndex(MqMessage mqMessage,long courseId){
        //任务id
        Long taskId = mqMessage.getId();
        MqMessageService mqMessageService = this.getMqMessageService();
        //取出第二个阶段状态
        int stageTwo = mqMessageService.getStageTwo(taskId);

        //任务幂等性处理
        if(stageTwo>0){
            log.debug("课程索引信息已写入,无需执行...");
            return;
        }
        //查询课程信息,调用搜索服务添加索引 ...

        //完成本阶段的任务
        mqMessageService.completedStageTwo(taskId);


    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/704463.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Shell】读取用户终端输入内容

授权 cd /Users/lion/Downloads/shell-test-demos chmod ux *.shread_user_enter.sh #!/bin/bashprintHelp() {echo "1. hello"echo "2. world"echo "0. exit" }printHelpnumber"" while [ -z $number ]; doread -p "enter a n…

干货,让微信群活跃的秘籍

微信用户数量庞大、使用率高,是很多企业/商家做社群营销的第一平台,所以目前有很多微信社群营销管理系统。我一直在用的一个多群管理工具---微信管理系统,对于新手来说,操作也是十分的简单易上手,每一步都有教程指导&a…

网站被黑挂马应该怎么解决

遇到网站被黑或者被挂马,其实都是很正常的现象,做网站的站长,几乎都有网站被黑的历史 遇到这种问题,首先: 检查网站源文件的日期,回顾一下在过去一段时间里面,你有没有对源文件做过改动&#x…

程序员性能之道,从使用perf开始!

一、perf简介 从2.6.31内核开始,Linux内核自带了一个性能分析工具perf,能够进行函数级与指令级的热点查找。通过它,应用程序可以利用 PMU,tracepoint 和内核中的特殊计数器来进行性能统计。它不但可以分析指定应用程序的性能问题…

Apikit 自学日记:使用脚本处理数据

脚本分为 前置脚本 和 后置脚本 两种,分别对应 API 请求前 和 返回数据后 的两个阶段。您可以通过编写 Javascript 代码,在 API 前置脚本中改变请求参数,或者是在 API 后置脚本中改变返回结果。 脚本常用于以下几种情况: API 请求…

【夜深人静学数据结构与算法 | 第十一篇】枚举算法

目录 前言: 枚举算法: 优点: 枚举算法的种类: 枚举算法案例: 343. 整数拆分 - 力扣(LeetCode) 12. 整数转罗马数字 - 力扣(LeetCode) 总结: 前言&…

video-05-videojs编写(全屏、非全屏)自定义控件!!!!

兄弟们!!看到这里,你马上就可以自定义控件了,想想是不是都激动啊,但是这篇文章重在思路及简单实现,仔细看。 目录 一、控件分类 二、实现方案(方案二最好) 2.1 方案1(…

前端Vue自定义轮播图swiper 轮播图dot 轮播图指示indicate

前端Vue自定义轮播图swiper 轮播图dot 轮播图指示indicate,下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id13193 效果图如下: ​ 编辑切换为居中 添加图片注释,不超过 140 字(可选) ​…

BACnet网关如何采集Modbus RTU设备转BACnet IP协议

BACnet标准是针对采暖、通风、空调、制冷控制设备设计的,同时也是为其他楼宇控制系统(例如照明,安保,消防等系统)的集成提供一个基本原则。 本文主要讲述了BACnet网关采集Modbus RTU设备(M140T),将Modbus …

servlet拓展-统一响应json功能、全局异常处理

servlet拓展-统一响应json功能、全局异常处理 一、baseservlet import com.fasterxml.jackson.databind.ObjectMapper; import teamwork.com.utils.ApiResult;import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.Htt…

解决IDEA连接数据库后,xml中写SQL语句不提醒数据库表字段问题

有时候我们新建了一个项目,或者新建了一个工程,明明为此项目或者工程连接了数据库,并且一切正确,但是在xml中书写SQL语句就是不提示数据库表中的相关字段,无论是表名也好,还是字段名也好,通通不…

信号链噪声分析15

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示:这里可以添加技术概要 模数转换器(ADC)将模拟量——现实世界中绝大部分现象的特征——转换为数字语言, 以便用于信息处理、计算、数据传输和控制系统。数模转换器(DAC)则用于将发送或存 储…

信号链噪声分析17

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 提示:这里可以添加技术概要 只是快速阅读 HarryNyquist 于 1924 年发表的《贝尔系统技术杂志》经典文章,并不 足以了解以其名字命名的该项准则的真正意义。当时,Nyquist 正致力于研…

解放双手,保障企业安全这一点很重要!

无论是交通管理部门、工业企业还是其他需要保障安全的场所,远程烟雾监控系统都是一个可靠的选择。 这远程烟雾监控是一项重要的技术解决方案,通过使用先进的传感器和监控系统来实时监测和管理烟雾情况,以提高安全性并及时应对潜在的风险。 客…

强化学习从基础到进阶–案例与实践[11]:AlphaStar论文解读、监督学习、强化学习、模仿学习、多智能体学习、消融实验

【强化学习原理项目专栏】必看系列:单智能体、多智能体算法原理项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍:【强化学习原理项目专栏】必看系列:单智能体、多智能体算法原理项目实战、相关技巧…

H5工具:产品生命周期四阶段及其重点关注数据指标

人有生老病死,同样,一个产品也有生命周期,叫产品生命周期(Product Life Cycle)。 产品生命周期指的是产品从进入市场到退出市场的周期性变化过程,它是指产品的市场寿命,而非产品的使用寿命。 一…

Linux自主学习 - C语言库Melon的快速入门

备注:ubuntu-20.04.3-desktop-amd64.iso 一、C语言库melon的安装 1、选择一个合适的文件夹用于下载Melon,本文以~/ProjectCode为例 在~/ProjectCode下输入git clone https://github.com/Water-Melon/Melon.git 然后,可以使用ls命令查看到Mel…

青大数据结构【2017】【简答】

关键字: 链式存储、顺序查找、折半查找、归并排序 采用链式存储结构,原因: 1)链式存储结构进行插入和删除只需要修改相应的指针就可以了,适合频繁的进行插入和删除操作。 2)链式存储不要求地址连续&…

VUE elementUI 自定义组件效验器失效

首先看一段源码 只要给el-form-item 设置了prop,并且model有prop属性,根据此计算属性就会触发fieldValue的取值,从而使效验生效,也就是说,初始化属性一定得写。 其次可以利用手动触发效验的方法灵活控制。如下所示 v…

Learn Mongodb DB常用命令及数据导入导出 ⑥

@作者 : SYFStrive @博客首页 : HomePage 📜: PHP MYSQL 📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗 📌:觉得文章不错可以点点关注 👉: