Nacos、Feign、Hystrix服务介绍安装、注册、启动、调用

一、Nacos

可以参考:服务发现、注册-搭建Nacos服务

1、基本概念

Nacos安装指南Windows和Linux(版本1.4.1和2.0.2)

(1)Nacos 是阿里巴巴推出来的一个新开源项目,是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

(2)常见的注册中心:

  1. Eureka(原生,2.0遇到性能瓶颈,停止维护)
  2. Zookeeper(支持,专业的独立产品。例如:dubbo)
  3. Consul(原生,GO语言开发)
  4. Nacos

相对于 Spring Cloud Eureka 来说,Nacos 更强大。Nacos = Spring Cloud Eureka + Spring Cloud Config

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

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

(3)Nacos是以服务为主要服务对象的中间件,Nacos支持所有主流的服务发现、配置和管理。

Nacos主要提供以下四大功能:

  1.  服务发现和服务健康监测
  2. 动态配置服务
  3. 动态DNS服务
  4. 服务及其元数据管理

(4)Nacos结构图

  • nacos注册中心
  • consumer消费者(调用方法的)
  • provider生产者(提供方法的)
  • 比如edu调用vod里的方法,edu就是消费者,vod就是生产者

(5)Nacos作用之一是实现微服务之间的调用,比如有edu服务和vod服务,要通过edu调用vod里的方法,按照微服务做法就要用到Nacos。

2、Nacos下载和安装

(1)下载地址和版本

下载地址:https://github.com/alibaba/nacos/releases

下载版本:nacos-server-1.1.4.tar.gz或nacos-server-1.1.4.zip,解压任意目录即可(不要选择beat版本,公测版本,不推荐上线生成

解压后, Windows里找到bin文件里的startup.cmd双击启动。

(2)启动nacos服务

如果启动失败,失败的原因之一是没有配置jdk环境,参考地址:https://www.cnblogs.com/konglxblog/p/14909489.html进行配置。

Linux/Unix/Mac

  • 启动命令(standalone代表着单机模式运行,非集群模式)
  • 启动命令:sh startup.sh -m standalone

Windows

  • 启动命令:cmd startup.cmd 或者双击startup.cmd运行文件。

启动成功如下图:

访问:http://localhost:8848/nacos(固定地址,在浏览器直接输入即可)

用户名密码:nacos/nacos

二、服务注册

参考官网地址:https://github.com/alibaba/spring-cloud-alibaba

1、在service模块配置pom

配置Nacos客户端的pom依赖(注意springboot和springcloud的版本兼容)
pom子模块文件(版本继承父模块)(新版的groupId是com.alibaba.cloud,可以参考上边的官网地址,https://github.com/alibaba/spring-cloud-alibaba
<!--服务注册-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
pom父模块文件版本
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <modules>
        <module>service</module>
        <module>common</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <groupId>com.stu</groupId>
    <artifactId>guli_parent</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1-SNAPSHOT</version>

    <name>guli_parent</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <guli.version>0.0.1-SNAPSHOT</guli.version>
        <mybatis-plus.version>3.0.5</mybatis-plus.version>
        <velocity.version>2.0</velocity.version>
        <swagger.version>2.7.0</swagger.version>
        <aliyun.oss.version>2.8.3</aliyun.oss.version>
        <jodatime.version>2.10.1</jodatime.version>
        <poi.version>3.17</poi.version>
        <commons-fileupload.version>1.3.1</commons-fileupload.version>
        <commons-io.version>2.6</commons-io.version>
        <httpclient.version>4.5.1</httpclient.version>
        <jwt.version>0.7.0</jwt.version>
        <aliyun-java-sdk-core.version>4.3.3</aliyun-java-sdk-core.version>
        <aliyun-sdk-oss.version>3.1.0</aliyun-sdk-oss.version>
        <aliyun-java-sdk-vod.version>2.15.5</aliyun-java-sdk-vod.version>
        <aliyun-java-vod-upload.version>1.4.11</aliyun-java-vod-upload.version>
        <aliyun-sdk-vod-upload.version>1.4.11</aliyun-sdk-vod-upload.version>
        <fastjson.version>1.2.28</fastjson.version>
        <gson.version>2.8.2</gson.version>
        <json.version>20170516</json.version>
        <commons-dbutils.version>1.7</commons-dbutils.version>
        <canal.client.version>1.1.0</canal.client.version>
        <docker.image.prefix>zx</docker.image.prefix>
        <cloud-alibaba.version>0.2.2.RELEASE</cloud-alibaba.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!--Spring Cloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--mybatis-plus 持久层-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>

            <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>${velocity.version}</version>
            </dependency>

            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <!--swagger ui-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger.version}</version>
            </dependency>

            <!--aliyunOSS-->
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>${aliyun.oss.version}</version>
            </dependency>

            <!--日期时间工具-->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
                <version>${jodatime.version}</version>
            </dependency>

            <!--xls-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>${poi.version}</version>
            </dependency>
            <!--xlsx-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>${poi.version}</version>
            </dependency>

            <!--文件上传-->
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>${commons-fileupload.version}</version>
            </dependency>

            <!--commons-io-->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>${commons-io.version}</version>
            </dependency>

            <!--httpclient-->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>${httpclient.version}</version>
            </dependency>

            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>${gson.version}</version>
            </dependency>

            <!-- JWT -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jwt.version}</version>
            </dependency>

            <!--aliyun-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>${aliyun-java-sdk-core.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>${aliyun-sdk-oss.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-vod</artifactId>
                <version>${aliyun-java-sdk-vod.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-vod-upload</artifactId>
                <version>${aliyun-java-vod-upload.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-sdk-vod-upload</artifactId>
                <version>${aliyun-sdk-vod-upload.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>org.json</groupId>
                <artifactId>json</artifactId>
                <version>${json.version}</version>
            </dependency>

            <dependency>
                <groupId>commons-dbutils</groupId>
                <artifactId>commons-dbutils</artifactId>
                <version>${commons-dbutils.version}</version>
            </dependency>

            <dependency>
                <groupId>com.alibaba.otter</groupId>
                <artifactId>canal.client</artifactId>
                <version>${canal.client.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
<!--    <build>-->
<!--        <plugins>-->
<!--            <plugin>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
<!--            </plugin>-->
<!--        </plugins>-->
<!--    </build>-->

</project>

2、添加服务配置信息

配置application.properties,在客户端微服务中添加注册Nacos服务的配置信息
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

yml版

server:
  port: 9130
spring:
  profiles:
    active: dev
  application:
    name: service-vod
  servlet:
    multipart:
      max-file-size: 1024MB # 最大上传单个文件大小:默认1M,设置为1G
      max-request-size: 10240MB # 最大置总上传的数据大小 :默认10M,设置为10G
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:9999
aliyun:
  vod:
    keyId: StXvxxxx
    keySecret: Vxxxx

3、添加Nacos客户端注解

在客户端微服务启动类中添加注解
@EnableDiscoveryClient

例子:

package com.stu.eduservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @EnableDiscoveryClient @ComponentScan(basePackages = {"com.stu"}) public class EduApplication { public static void main(String[] args) { SpringApplication.run(EduApplication.class,args); } }

4、启动客户端微服务

启动注册中心
启动已注册的微服务,可以在Nacos服务列表中看到被注册的微服务

三、服务调用-Feign

基本概念

  • Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。
  • Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
  • Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
  • Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。
  • Spring Cloud Feign帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。

1、需求

删除课时的同时删除云端视频

2、在service模块添加pom依赖

<!--服务调用-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

3、在调用端的启动类添加注解(@EnableFeignClients)

package com.stu.eduservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@EnableDiscoveryClient//nacos注册
@EnableFeignClients //服务调用-Feign(调用端加这个注解,就是edu调用vod)
@ComponentScan(basePackages = {"com.stu"})
public class EduApplication {
    public static void main(String[] args) {

        SpringApplication.run(EduApplication.class,args);
    }
}

4、创建包和接口

创建client包
@FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致。
@GetMapping注解用于对被调用的微服务进行地址映射。
@PathVariable注解一定要指定参数名称,否则出错
@Component注解防止,在其他位置注入CodClient时idea报错

注意这个接口是创建在edu层的,也就是调用端(edu调用vod里的方法)

package com.stu.eduservice.client;

import com.stu.commonutils.ResultData;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;
//这个是vod属性文件里的服务名,即spring.application.name=service-vod,
//fallback是实现类的class
@FeignClient(name = "service-vod",fallback = VodFileDegradeFeignClient.class)
@Component
public interface VodClient {

    //根据id删除阿里云视频(映射路径是Vod业务里controller层的类映射+方法上的映射,即
    // @RequestMapping("/eduvod/video") + @DeleteMapping("removeVideo/{id}"))

    /* @FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致。
    @GetMapping注解用于对被调用的微服务进行地址映射。
    @PathVariable注解一定要指定参数名称,否则出错
    @Component注解防止,在其他位置注入CodClient时idea报错*/

    @DeleteMapping("/eduvod/video/removeVideo/{id}")
    public ResultData removeAlyVideo(@PathVariable("id") String id);

    //根据id批量删除阿里云视频
    @DeleteMapping("eduvod/video/removeVideoBatch")
    public ResultData removeVideoBatch(@RequestParam("videoIdList") List<String> videoIdList);
}

vod里的controller

package com.stu.vod.controller;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.vod.model.v20170321.GetVideoPlayAuthRequest;
import com.aliyuncs.vod.model.v20170321.GetVideoPlayAuthResponse;
import com.stu.commonutils.ResultData;
import com.stu.servicebase.exceptionHandler.GuliException;
import com.stu.vod.service.VodService;
import com.stu.vod.utils.ConstantVodUtils;
import com.stu.vod.utils.InitVodClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@RestController
@RequestMapping("/eduvod/video")

//@CrossOrigin
public class VodController {

    @Autowired
    private VodService vodService;

    //上传视频到阿里云
    @PostMapping("uploadAlyVideo")
    public ResultData uploadAlyVideo(MultipartFile file){
        //返回视频id
        String videoId = vodService.uploadAlyVideo(file);
        return ResultData.success().data("videoId",videoId);
    }
    //根据id删除阿里云视频
    @DeleteMapping("removeVideo/{id}")
    public ResultData removeVideo(@PathVariable String id){
        //返回视频id
        String videoId = vodService.removeVideo(id);
        return ResultData.success();
    }
    //根据id批量删除阿里云视频
    @DeleteMapping("removeVideoBatch")
    public ResultData removeVideoBatch(@RequestParam("videoIdList") List<String> videoIdList){
        //返回视频id
        vodService.removeVideoBatch(videoIdList);
        return ResultData.success();
    }

    //根据视频id获取视频的播放凭证
    @GetMapping("getAlyVideoAuth/{id}")
    public ResultData getAlyVideoAuth(@PathVariable String id){

        try {
            //1.1创建初始化对象
            //创建初始化对象
            DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.KEY_ID,ConstantVodUtils.KEY_SECRET);
            //1.2创建获取视频地址response和request对象
            GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
            GetVideoPlayAuthResponse response = new GetVideoPlayAuthResponse();
            //1.3向request对象里面设置视频id
            request.setVideoId(id);
            //1.4调用初始化对象里的方法,取得凭证
            response = client.getAcsResponse(request);
            String playAuth = response.getPlayAuth();
            return ResultData.success().data("playAuth",playAuth);
        } catch (ClientException e) {
            throw new GuliException(20001,"获取凭证失败");
        }

    }
}

vod的service接口

package com.stu.vod.service;

import org.springframework.web.multipart.MultipartFile;

import java.util.List;

public interface VodService {
    String uploadAlyVideo(MultipartFile file);

    String removeVideo(String id);

    void removeVideoBatch(List<String> videoIdList);
}

vod的service实现类

package com.stu.vod.service.impl;

import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.req.UploadStreamRequest;
import com.aliyun.vod.upload.resp.UploadStreamResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.vod.model.v20170321.DeleteVideoRequest;
import com.stu.servicebase.exceptionHandler.GuliException;
import com.stu.vod.service.VodService;
import com.stu.vod.utils.ConstantVodUtils;
import com.stu.vod.utils.InitVodClient;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class VodServiceImpl implements VodService {

    @Override
    public String uploadAlyVideo(MultipartFile file) {
        try {
            //accessKeyId, accessKeySecret
            //fileName:上传文件原始名称
            // 01.03.09.mp4
            String fileName = file.getOriginalFilename();
            //title:上传之后显示名称
            String title = fileName.substring(0, fileName.lastIndexOf("."));
            //inputStream:上传文件输入流
            InputStream inputStream = file.getInputStream();
            UploadStreamRequest request = new UploadStreamRequest(ConstantVodUtils.KEY_ID,ConstantVodUtils.KEY_SECRET, title, fileName, inputStream);

            UploadVideoImpl uploader = new UploadVideoImpl();
            UploadStreamResponse response = uploader.uploadStream(request);

            String videoId = null;
            if (response.isSuccess()) {
                videoId = response.getVideoId();
            } else { //如果设置回调URL无效,不影响视频上传,可以返回VideoId同时会返回错误码。其他情况上传失败时,VideoId为空,此时需要根据返回错误码分析具体错误原因
                videoId = response.getVideoId();
            }
            return videoId;
        }catch(Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String removeVideo(String id) {
        try {

            //1初始化对象
            DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.KEY_ID,
                    ConstantVodUtils.KEY_SECRET);
            //2创建删除视频request对象
            DeleteVideoRequest request = new DeleteVideoRequest();
            //3向request设置视频id
            request.setVideoIds(id);
            //4调用初始化对象的方法删除视频
            client.getAcsResponse(request);

            return id;
        } catch (Exception e) {
            e.printStackTrace();
            throw new GuliException(20001,"删除视频失败");
        }
    }

    @Override
    public void removeVideoBatch(List<String> videoIdList) {
        try {

            /*//方式1Java 8使用String.join()函数
            String str1 = String.join(",", videoIdList);
            System.out.println("str1 =" +str1);

            //方式2 使用org.apache.commons.lang.StringUtils.join
            String str2 = org.apache.commons.lang.StringUtils.join(videoIdList.toArray(), ",");
            System.out.println("str2="+str2);*/
            //将集合转换为分割的字符串,比如A,B,C,D,E格式
            String idsStr = String.join(",", videoIdList.stream().distinct().collect(Collectors.toList()));
            //1初始化对象
            DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.KEY_ID,
                    ConstantVodUtils.KEY_SECRET);
            //2创建删除视频request对象
            DeleteVideoRequest request = new DeleteVideoRequest();
            //3向request设置视频id
            request.setVideoIds(idsStr);
            //4调用初始化对象的方法删除视频
            client.getAcsResponse(request);

        } catch (Exception e) {
            e.printStackTrace();
            throw new GuliException(20001,"删除视频失败");
        }
    }
} 

5、调用微服务

在调用端的EduVideoServiceImpl实现删除阿里云的视频和小节
package com.stu.eduservice.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.stu.commonutils.ResultData;
import com.stu.eduservice.client.VodClient;
import com.stu.eduservice.entity.EduVideo;
import com.stu.eduservice.mapper.EduVideoMapper;
import com.stu.eduservice.service.IEduVideoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stu.servicebase.exceptionHandler.GuliException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程视频 服务实现类
 * </p>
 *
 * @author stu
 * @since 2021-05-29
 */
@Service
public class EduVideoServiceImpl extends ServiceImpl<EduVideoMapper, EduVideo> implements IEduVideoService {

    @Autowired
    private VodClient codClient;
    //1根据课程id先删除小节
    //删除小节,需要删除对应的视频文件
    @Override
    public boolean removeVideoByCourseId(String courseId) {
        //1.根据课程id查询所有视频id
        QueryWrapper<EduVideo> wrapperVidoe = new QueryWrapper();
        wrapperVidoe.eq("course_id",courseId);
        wrapperVidoe.select("video_source_id");//设置只查出video_source_id这一列
        List<EduVideo> videoList = baseMapper.selectList(wrapperVidoe);
        //将List<EduVideo>转换成List<String>
        List<String> idsList = null;
        if(null != videoList && videoList.size()>0){
            idsList = new ArrayList<>();
            for(EduVideo vo:videoList){
                if(!StringUtils.isEmpty(vo.getVideoSourceId())){
                    idsList.add(vo.getVideoSourceId());
                }
            }
            if(null != idsList && idsList.size()>0){
                //删除阿里云的视频
                ResultData result = codClient.removeVideoBatch(idsList);

                //删除失败20001
                if(result.getCode() == 20001){
                    throw new GuliException(20001,"删除视频失败,熔断器处理。。。");
                }
            }
        }

        QueryWrapper<EduVideo> wrapper = new QueryWrapper();
        wrapper.eq("course_id",courseId);
        return baseMapper.delete(wrapper)>0;
    }

    @Override
    public boolean removeVodAndVideoByCourseId(String id) {
        //根据小节id获取视频id,调用方法实现删除
        EduVideo eduVideo =  baseMapper.selectById(id);
        //取得视频id
        String videoSourceId = eduVideo.getVideoSourceId();

        //根据视频id删除阿里云视频
        if(!StringUtils.isEmpty(videoSourceId)){
            ResultData result = codClient.removeAlyVideo(videoSourceId);

            //删除失败20001
            if(result.getCode() == 20001){
                throw new GuliException(20001,"删除视频失败,熔断器处理。。。");
            }
        }
        //先删除视频在删除小节,因为删除视频要先根据小节查出视频id在删除视频
        baseMapper.deleteById(id);
        return true;
    }


}

四、Hystrix基本概念

官网:https://github.com/Netflix/Hystrix/wiki/Configuration

1、Spring Cloud调用接口过程

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

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

 
(1)接口化请求调用当调用被@FeignClient注解修饰的接口时,在框架内部,将请求转换成Feign的请求实例feign.Request,交由Feign框架处理(在消费者指定调用的生产者的服务名称和接口地址,即在edu指定要调用的vod的服务名称和接口地址)。

(2)Feign :转化请求Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,封装了Http调用流程(Feign找到调用的服务名称,根据服务名称找到接口地址)。

(3)Hystrix:熔断处理机制 Feign的调用关系,会被Hystrix代理拦截,对每一个Feign调用请求,Hystrix都会将其包装成HystrixCommand,参与Hystrix的流控和熔断规则。如果请求判断需要熔断,则Hystrix直接熔断,抛出异常或者使用FallbackFactory返回熔断Fallback结果;如果通过,则将调用请求传递给Ribbon组件(edu服务调用vod服务,假如vod服务突然不好用了,Hystrix就不让他继续调用,断开调用的连接)。

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

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

2、Hystrix概念

Hystrix 是一个供分布式系统使用,提供延迟和容错功能,保证复杂的分布系统在面临不可避免的失败时,仍能有其弹性。

比如系统中有很多服务,当某些服务不稳定的时候,使用这些服务的用户线程将会阻塞,如果没有隔离机制,系统随时就有可能会挂掉,从而带来很大的风险。SpringCloud使用Hystrix组件提供断路器、资源隔离与自我修复功能。下图表示服务B触发了断路器,阻止了级联失败

 

五、feign结合Hystrix使用

改造service-edu模块

1、在service的pom中添加依赖(红色加粗部分)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>guli_parent</artifactId>
        <groupId>com.stu</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.stu</groupId>
    <artifactId>service</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <modules>
        <module>service-edu</module>
        <module>service-oss</module>
        <module>service-vod</module>

    </modules>

    <dependencies>
        <dependency>
            <groupId>com.stu</groupId>
            <artifactId>service-base</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

        <!--hystrix依赖,主要是用  @HystrixCommand -->
      <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

        <!--服务注册-->
       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
       </dependency>
        <!--服务调用-->
       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.10</version>
        </dependency>

        <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
        </dependency>

        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>

        <!--lombok用来简化实体类:需要安装lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--xls-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
        </dependency>

        <!--httpclient-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!--commons-io-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <!--gson-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

2、在配置文件中添加hystrix配置(红色加粗部分)

# 服务端口
server.port=8001
# 服务名
spring.application.name=service-edu

# 环境设置:dev、test、prod
spring.profiles.active=dev

# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/zxjy?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/stu/eduservice/mapper/xml/*.xml

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

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

# 设置日志级别
#logging.level.root=info
#mybatis日志
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

3、在service-edu的client包里面修改接口并创建熔断器实现类

接口
package com.stu.eduservice.client;

import com.stu.commonutils.ResultData;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;
//这个是vod属性文件里的服务名,即spring.application.name=service-vod,
//fallback是实现类的class
@FeignClient(name = "service-vod",fallback = VodFileDegradeFeignClient.class)
@Component
public interface VodClient {

    //根据id删除阿里云视频(映射路径是Vod业务里controller层的类映射+方法上的映射,即
    // @RequestMapping("/eduvod/video") + @DeleteMapping("removeVideo/{id}"))

    /* @FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致。
    @GetMapping注解用于对被调用的微服务进行地址映射。
    @PathVariable注解一定要指定参数名称,否则出错
    @Component注解防止,在其他位置注入CodClient时idea报错*/

    @DeleteMapping("/eduvod/video/removeVideo/{id}")
    public ResultData removeAlyVideo(@PathVariable("id") String id);

    //根据id批量删除阿里云视频
    @DeleteMapping("eduvod/video/removeVideoBatch")
    public ResultData removeVideoBatch(@RequestParam("videoIdList") List<String> videoIdList);
}

实现类

package com.stu.eduservice.client;

import com.stu.commonutils.ResultData;
import org.springframework.stereotype.Component;

import java.util.List;
/*熔断器的实现类*/
@Component
public class VodFileDegradeFeignClient implements VodClient {

    //如果删除接口报错,就会执行这里面的方面,比如删除视频的时候,只启动edu服务,断掉vod服务,就会执行这里的方法
    @Override
    public ResultData removeAlyVideo(String id) {
        return ResultData.error().message("删除视频出错了");
    }

    @Override
    public ResultData removeVideoBatch(List<String> videoIdList) {
        return ResultData.error().message("批量删除视频出错了");
    }
}

作者:
出处:https://www.cnblogs.com/konglxblog//
版权:本文版权归作者和博客园共有
转载:欢迎转载,文章中请给出原文连接,此文章仅为个人知识学习分享,否则必究法律责任

 
posted @ 2021-06-23 22:06  程序员小明1024  阅读(831)  评论(0编辑  收藏  举报