谷粒学院16万字笔记+1600张配图(十二)——课程管理

项目源码与所需资料
链接:https://pan.baidu.com/s/1azwRyyFwXz5elhQL0BhkCA?pwd=8z59
提取码:8z59

demo12-课程管理

1.微服务

1.1微服务介绍

  • 微服务是架构风格
  • 把一个项目拆分成独立的多个服务,多个服务是独立运行,每个服务占用独立进程(我们前面的8001、8002、8003端口就是这样的)

1.2微服务与单体架构区别

  • 单体架构所有的模块全都耦合在一块,代码量大,维护困难
    • 微服务每个模块就相当于一个单独的项目,代码量明显减少,遇到问题也相对来说比较好解决
  • 单体架构所有的模块都共用一个数据库,存储方式比较单一
    • 微服务每个模块都可以使用不同的存储方式(比如有的用redis,有的用mysql等),数据库也是单个模块对应自己的数据库
  • 单体架构所有的模块开发所使用的技术一样
    • 微服务每个模块都可以使用不同的开发技术,开发模式更灵活

1.3什么样的项目适合微服务

微服务可以按照业务功能本身的独立性来划分,如果系统提供的业务是非常底层的,如:操作系统内核、存储系统、网络系统、数据库系统等等,这类系统都偏底层,功能和功能之间有着紧密的配合关系,如果强制拆分为较小的服务单元,会让集成工作量急剧上升,并且这种人为的切割无法带来业务上的真正的隔离,所以无法做到独立部署和运行,也就不适合做成微服务了

1.4微服务开发框架

目前微服务的开发框架,最常用的有以下四个:

Spring Cloud:http://projects.spring.io/spring-cloud(现在非常流行的微服务架构)

Dubbo:http://dubbo.io

Dropwizard:http://www.dropwizard.io(关注单个微服务的开发)

Consul、etcd&etc.(微服务的模块)

2.Spring Cloud

2.1什么是Spring Cloud

  • Spring Cloud并不像Spring、Mybatis、JavaWeb、Servlet…那样是一种技术,而是很多技术的总称,或者说是很多技术的集合
  • Spring Cloud里面有很多框架(技术),我们使用Spring Cloud里面这些框架实现微服务操作
  • 想要使用Spring Cloud需要依赖于框架SpringBoot

2.2Spring Cloud和Spring Boot的区别与联系

1.区别:

  • Spring Boot就是Spring,是快速构建Spring的脚手架
  • Spring Cloud是一系列框架的总称

2.联系:

  • 使用Spring Cloud需要基于Spring Boot

3.微服务架构:

一提到微服务架构我们就要想到Spring Cloud而不是Spring Boot,Spring Boot只是里面的技术,真正的微服务架构是Spring Cloud

2.3Spring Cloud相关基础服务组件

  • 服务发现——Netflix Eureka(Nacos)
    • 服务发现就是注册中心的概念
  • 服务调用——Netflix Feign
  • 熔断器——Netflix Hystrix
  • 服务网关——Spring Cloud GateWay
  • 分布式配置——Spring Cloud Config(Nacos)
  • 消息总线——Spring Cloud Bus(Nacos)

服务发现、分布式配置、消息总线现在都用Nacos来实现

2.4Spring Cloud的版本

1.Spring Cloud并没有熟悉的数字版本号,而是对应一个开发代号:

Cloud代号 Boot版本(train) Boot版本(tested) lifecycle
Angle 1.2.x incompatible with 1.3 EOL in July 2017
Brixton 1.3.x 1.4.x 2017-07卒
Camden 1.4.x 1.5.x -
Dalston 1.5.x not expected 2.x -
Edgware 1.5.x not expected 2.x -
Finchley 2.0.x not expected 1.5.x -
Greenwich 2.1.x
Hoxton 2.2.x

开发代号看似没有什么规律,但实际上首字母是有顺序的,比如:Dalston版本,我们可以简称D版本;Edgware版本,我们可以简称E版本。这样的话这些版本代码其实就是ABCDEFGH

2.我们项目中用的Spring Boot是2.2.1,所以使用Cloud时需要使用Hoxton版本

在这里插入图片描述

在这里插入图片描述

3.Spring Cloud小版本分为:

  • SNAPSHOT:快照版本,随时可能修改,所以我们一般不使用这个版本
  • M:MileStone,M1表示第1个里程碑版本,一般同时标注PRE,表示预览版本
  • SR:Service Release,SR1表示第1个正式版本,一般同时标注GA:(GenerallyAvailable),表示稳定版本

一般我们肯定用稳定版本,如果没有稳定版本就用SR,如果没有SR就用M,别使用SNAPSHOT

3.注册中心

3.1引出注册中心

1.我们在"demo09-课程管理"的"8.3删除小节"说过,小节下是有视频的,我们删除小节时需要顺带将阿里云中的该视频删掉

2.我们可以在service_edu模块的控制器EduVideoController的updateVideo方法中写删除阿里云视频的业务逻辑(或者在业务层写这个业务逻辑也可以)

3.但是我们还是用微服务的方式来实现这个业务

4.微服务:把我们的项目划分为多个模块(或者说多个服务),每个服务都是独立运行的,每个服务中专门做一些功能,比如:我们的service_edu专门做课程、service_oss专门做阿里云oss、service_vod专门做视频

5.我们在"demo11-课程管理"的"7.3控制层"做了删除阿里云视频的业务逻辑,所以我们现在,如果不使用微服务方式实现,那么我们可以直接将"7.3控制层"中删除阿里云视频的业务逻辑代码复制粘贴到service_edu模块

6.但我们现在要使用微服务方式实现,那么就需要在service_edu模块中调用service_vod模块中删除阿里云视频的方法,这样的实现方式就叫做微服务

7.但是service_edu模块和service_vod模块是独立运行的,想要实现在service_edu模块调用service_vod模块的方法,有一种实现方式:在service_edu模块的pom.xml中引入service_vod依赖,但是这样的话这两个模块就有关联了,这就不叫微服务了

8.正确的实现方式是使用Spring Cloud模块中的技术:注册中心

9.我们将service_edu模块和service_vod模块都注册到注册中心里面,这样就可以实现在service_edu模块调用service_vod模块的方法

3.1注册中心介绍

1.如果我们想要实现不同的微服务模块之间的调用,就需要把这些模块在注册中心进行注册

2.常见的注册中心:

  • Eureka(原生,2.0遇到性能瓶颈,停止维护)

  • Zookeeper(支持,专业的独立产品。例如:dubbo)

  • Consul(原生,GO语言开发)

  • Nacos

    • 相对于Spring Cloud Eureka来说,Nacos更强大:Nacos = Spring Cloud Eureka + Spring Cloud Config、Spring Cloud Bus

      Nacos可以与Spring、Spring Boot,、Spring Cloud 集成,并能代替Spring Cloud Eureka、Spring Cloud Config、Spring Cloud Bus

      通过Nacos Server和spring-cloud-starter-alibaba-nacos-discovery实现服务的注册与发现

3.Nacos执行流程:

在这里插入图片描述

  • 注册时用到模块的ip和端口号
  • 生产者就是提供方法的;消费者就是调用方法的。我们需要实现在service_edu模块调用service_vod模块的方法,那么此时service_edu就是消费者;service_vod就是生产者

4.Nacos安装和服务注册

4.1安装与访问

1.安装nacos:

nacos提供在了资料中,解压到任意目录即可

在这里插入图片描述

2.启动nacos服务

windows系统双击bin目录下的startup.cmd启动nacos服务;linux系统双击bin目录下的startup.sh启动nacos服务

在这里插入图片描述

在这里插入图片描述

3.访问:

访问地址是:http://localhost:8848/nacos

账号密码都是nacos

4.2服务注册(service_edu)

服务注册以service_edu为例:

1.在service_edu中引入依赖,因为我们后面service模块下的子模块有很多都要注册到注册中心,所以我们选择在service模块的pom.xml中引入依赖

我们在"demo03-后台讲师管理模块"的"2.2.3创建子模块"的第5步将这个依赖注释掉了,现在我们将这个依赖打开即可(别忘了刷新maven)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.在service_edu的配置文件application.properties中配置nacos地址(即nacos的ip和端口号)

# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

在这里插入图片描述

3.在service_edu的启动类中添加Nacos客户端注解@EnableDiscoveryClient,目的是让注册生效:

在这里插入图片描述

4.重启后端项目,在地址栏输入http://localhost:8848/nacos,登录后在"服务列表"菜单就可以看到我们成功将service_edu服务注册进来了

在这里插入图片描述

为什么截图中服务名是service-edu?因为我们在配置文件中配置了服务名为service_edu

在这里插入图片描述

为什么我们配置的服务名是service-edu而不是service_edu?因为如果用下划线可能会有问题,这里用横杠而不是下划线

4.3服务注册(service_vod)

1.在service_vod的配置文件application.properties中配置nacos地址(即nacos的ip和端口号)

# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

在这里插入图片描述

2.在service_vod的启动类中添加Nacos客户端注解@EnableDiscoveryClient,目的是让注册生效:

在这里插入图片描述

3重启后端项目,在地址栏输入http://localhost:8848/nacos,登录后在"服务列表"菜单就可以看到我们成功将service_vod服务注册进来了

在这里插入图片描述

4.4问题

1.重启service_oss服务后控制台报错了

在这里插入图片描述

2.这是因为我们在service模块的pom.xml中引入了nacos依赖,所以启动service_oss模块时就会去找nacos注册中心,但是我们并没有在service_oss中配置注册,所以就找不到,所以就会报错。

所以我们需要将service_oss服务也注册到注册中心,具体步骤在"4.3服务注册(service_vod)",自行进行配置吧

5.删除小节时删除视频(服务调用)

5.1Feign

想要实现服务之间的调用需要用到Spring Cloud中的组件Feign

5.2实现服务调用

实现服务调用的前提是我们需要先将互相调用的服务在nacos中进行注册,这一步我们在前面已经实现了

1.在service模块的pom.xml中引入服务调用的依赖

我们在"demo03-后台讲师管理模块"的"2.2.3创建子模块"的第5步将这个依赖注释掉了,现在我们将这个依赖打开即可(别忘了刷新maven)

在这里插入图片描述

在这里插入图片描述

2.在调用端(service_edu)的启动类添加注解@EnableFeignClients

在这里插入图片描述

3.在调用端的eduservice包下创建包client,然后在client包下创建接口VodClient

@FeignClient("service-vod")
@Component
public interface VodClient {
    
    

    //定义调用的方法路径
    @DeleteMapping("/eduvod/video/removeAlyVideo/{id}")
    public R removeAlyVideo(@PathVariable("id") String id);
}

在这里插入图片描述

  • @FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致
  • @PathVariable注解一定要指定参数名称,否则出错

4.完善调用端的控制器EduVideoController中deleteVideo方法以实现删除小节时删除阿里云视频

①先在调用端的控制器EduVideoController中注入vodClient

//注入vodClient
@Autowired
private VodClient vodClient;

在这里插入图片描述

②然后在deleteVideo方法中添加如下代码

/**
 * 1.删除小节时删除阿里云视频
 * 具体实现:根据小节id得到视频id,然后调用方法实现视频删除
 */
//1.1根据小节id得到视频id
EduVideo eduVideo = videoService.getById(id);
String videoSourceId = eduVideo.getVideoSourceId();
//1.2根据视频id,远程调用实现视频删除(线判断id不为空才调用方法删除)
if (!StringUtils.isEmpty(videoSourceId)) {
    
    
    vodClient.removeAlyVideo(videoSourceId);
}

在这里插入图片描述

截图中第50行使用的工具类StringUtils是spring包下的,别导错包了

5.3测试

重启后端项目后在9528端口自行测试,我测试没问题

6.删除课程时删除视频(接口)

6.1分析

1.一个课程有多个章节,一个章节有很多小节,每个小节都有一个视频(也有可能没有给小节添加视频),也就是说删除课程时需要删除多个视频

2.我们在service_vod的控制器VodController中编写过"根据视频id删除阿里云中的视频"的方法,那么删除多个视频时可以这样:写一个循环,循环多次调用这个方法,这种方式是完全可行的
在这里插入图片描述

3.我们不使用刚刚说的那种方式,我们这里直接重新编辑一个方法,这个方法是"根据多个视频id删除阿里云中的视频"

6.2控制层

在service_vod的控制器VodController中编写方法"根据多个视频id删除阿里云中的视频"

//根据多个视频id删除阿里云中的视频
@DeleteMapping("delete-batch")
public R deleteBatch(@RequestParam("videoIdList") List videoIdList) {
    
    
    vodService.removeMoreAlyVideo(videoIdList);
    return R.ok();
}

在这里插入图片描述

截图中第61行的@RequestParam("videoIdList")是为了表明参数是videoIdList,老师说了不加这个代码也是可以的

6.3业务层接口

在service_vod的业务层接口VodService中定义抽象方法

//根据多个视频id删除阿里云中的视频
void removeMoreAlyVideo(List videoIdList);

在这里插入图片描述

6.4业务层实现类

在service_vod的业务层实现类VodServiceImpl中实现上一步定义的抽象方法

//根据多个视频id删除阿里云中的视频
@Override
public void removeMoreAlyVideo(List videoIdList) {
    
    
    try {
    
    
        //1.创建初始化对象
        DefaultAcsClient client = InitVodClient.initVodClient(
            ConstantVodUtils.ACCESS_KEY_ID,
            ConstantVodUtils.ACCESS_KEY_SECRET);

        //2.创建删除的request
        DeleteVideoRequest request = new DeleteVideoRequest();

        //3.向request对象里面设置视频id
        //①需要先将videoIdList集合中的id遍历为1,2,3的形式
        String videoIds = StringUtils.join(videoIdList.toArray(), ",");
        //②设置视频id
        request.setVideoIds(videoIds);

        //4.调用初始化对象里面的方法,实现删除
        client.getAcsResponse(request);
    } catch(Exception e) {
    
    
        e.printStackTrace();
        throw new GuliException(20001, "删除视频失败");
    }
}

在这里插入图片描述

  • StringUtils.join(videoIdList.toArray(), ",")表示先将集合转为数据,然后将数组中的数据按照逗号(,)进行分割
    • 这里的StringUtils工具类使用的是org.apache.commons.lang包下的

7.删除课程时删除视频(远程调用)

1.在调用端(service_edu)的VodClient接口中定义调用"根据多个视频id删除阿里云中的视频"的方法

//定义调用"根据多个视频id删除阿里云中的视频"的方法
@DeleteMapping("/eduvod/video/delete-batch")
public R deleteBatch(@RequestParam("videoIdList") List videoIdList);

在这里插入图片描述

2.我们在service_edu的业务层实现类EduVideoServiceImpl的removeVideoByCourseId中说过删除小节前需要先删除小节下的视频文件(TODO)

在这里插入图片描述

3.我们在removeVideoByCourseId方法中完善业务逻辑

//TODO 删除小节前需要先删除小节下的视频文件
//1.根据课程id查询课程中所有的视频id(视频id在小节表中,所以我们查小节表edu_video)
//①得到List<EduVideo>
QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>();
wrapperVideo.eq("course_id", courseId);
wrapperVideo.select("video_source_id"); //查询指定的列
List<EduVideo> eduVideoList = baseMapper.selectList(wrapperVideo);
//②将List<EduVideo>变为List<String>
List<String> videoIds = new ArrayList<>();
for (int i = 0; i < eduVideoList.size(); i++) {
    
    
    EduVideo eduVideo = eduVideoList.get(i);
    String videoSourceId = eduVideo.getVideoSourceId();
    //将不为空的视频id放到videoIds集合中
    if (!StringUtils.isEmpty(videoSourceId)) {
    
    
        videoIds.add(videoSourceId);
    }
}
//2.远程调用:根据多个视频id删除阿里云中的视频
//判断:如果课程下没有一个视频,那就不用调用这个方法了
if (videoIds.size() > 0) {
    
    
    vodClient.deleteBatch(videoIds);
}

在这里插入图片描述

4.在实现类EduVideoServiceImpl中注入VodClient

@Autowired
private VodClient vodClient;

在这里插入图片描述

5.测试

1.重启后端项目,可以看到service_edu项目报错了:Could not resolve element type of Iterable type @org.springframework.web.bind.annotation.RequestParam java.util.List<?>. Not declared?

在这里插入图片描述

2.这是因为我们没有给List集合加泛型,所以会转换失败,解决方法法:

①给service_vod项目的控制器VodController的deleteBatch方法的List集合加泛型(老师说了这个地方不加也是可以的,但是为了规范还是加了)

在这里插入图片描述

②给service_edu项目的VodClient接口的deleteBatch方法的List集合加泛型

在这里插入图片描述

3.再次重启后端项目就没问题了,后面的在9528端口自行测试吧

8.Spring Cloud调用接口流程

Spring Cloud 在接口调用上,大致会经过如下几个组件配合:

Feign ----->Hystrix —>Ribbon —>Http Client(apache http components 或者 Okhttp) 具体交互流程上,如下图所示:

在这里插入图片描述

  • 接口化请求调用:当调用被@FeignClient注解修饰的接口时,在框架内部,将请求转换成Feign的请求实例feign.Request,交由Feign框架处理。

  • Feign:转化请求Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,封装了Http调用流程。

  • Hystrix:熔断处理机制 Feign的调用关系,会被Hystrix代理拦截,对每一个Feign调用请求,Hystrix都会将其包装成HystrixCommand,参与Hystrix的流控和熔断规则。如果请求判断需要熔断,则Hystrix直接熔断,抛出异常或者使用FallbackFactory返回熔断Fallback结果;如果通过,则将调用请求传递给Ribbon组件。

  • Ribbon:服务地址选择 当请求传递到Ribbon之后,Ribbon会根据自身维护的服务列表,根据服务的服务质量,如平均响应时间,Load等,结合特定的规则,从列表中挑选合适的服务实例,选择好机器之后,然后将机器实例的信息请求传递给Http Client客户端,HttpClient客户端来执行真正的Http接口调用;

  • HttpClient:Http客户端,真正执行Http调用根据上层Ribbon传递过来的请求,已经指定了服务地址,则HttpClient开始执行真正的Http请求

9.熔断器

9.1介绍

  • 如果在service_edu模块调用service_vod模块的方法时,突然service_vod服务器宕机了(挂掉了),此时就要用到熔断器使其不让访问service_vod
  • 假如在service_edu中设置请求时间最多为5秒,超时就报错请求。如果在service_edu模块调用service_vod模块的方法时,已经过5秒仍没有数据返回,此时就会报错,此时我们就需要用熔断器来延迟请求时间

9.2在项目中整合熔断器

1.添加依赖

在service模块的pom.xml中添加hystrix依赖和ribbon依赖

我们在"demo03-后台讲师管理模块"的"2.2.3创建子模块"的第5步将这两个依赖注释掉了,现在我们将这两个依赖打开即可(别忘了刷新maven)

在这里插入图片描述

在这里插入图片描述

2.在调动端(service_edu)的配置文件application.properties中开启熔断器机制

#开启熔断机制
feign.hystrix.enabled=true
# 设置hystrix超时时间,默认1000ms
# hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000

在这里插入图片描述

feign.hystrix.enabled的值默认是false,也就是不开启熔断机制

3.我们已经在调用端的client包下创建了接口VodClient,现在我们在client包下创建这个接口的实现类VodFileDegradeFeignClient

@Component
public class VodFileDegradeFeignClient implements VodClient{
    
    
    @Override
    public R removeAlyVideo(String id) {
    
    
        return R.error().message("删除视频出错了");
    }

    @Override
    public R deleteBatch(List<String> videoIdList) {
    
    
        return R.error().message("删除多个视频出错了");
    }
}

在这里插入图片描述

如果调用成功就不会执行实现类VodFileDegradeFeignClient中的方法,如果调用失败就会执行实现类中的方法

4.修改调用端的VodClient接口中的注解@FeignClient

修改前@FeignClient("service-vod")

修改后@FeignClient(name = "service-vod", fallback = VodFileDegradeFeignClient.class)

在这里插入图片描述

9.3测试

1.首先我们知道据视频id删除阿里云中的视频的方法removeAlyVideo删除成功状态码是20000,删除失败状态码是20001

在这里插入图片描述

在这里插入图片描述

2.注意红方框圈起来的代码

在这里插入图片描述

将上述红方框圈起来的代码改为如下代码

R result = vodClient.removeAlyVideo(videoSourceId);
if (result.getCode() == 20001) {
    
    
    throw new GuliException(20001, "根据单个视频id删除视频失败,熔断器...");
}

在这里插入图片描述

3.重启后端服务,随便添加一个课程,课程下随便添加一个章节,章节下随便添加一个小节,小节下随便上传一个视频

在这里插入图片描述

4.把service_vod服务停掉

5.点击这个小节的"删除"按钮

在这里插入图片描述

在这里插入图片描述

6.看到如下错误,这就是熔断器

在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/maxiangyu_/article/details/127028519