1.Eureka

1.1基本概念

Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目 spring-cloud-netflix中,实现SpringCloud的服务发现功能。

Eureka包含两个组件: Eureka ServerEureka Client

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也包含一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会 向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有 接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90 秒)。

Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。 

1.2 Eureka的自我保护模式

如果在Eureka Server的首页看到以下这段提示,则说明Eureka已经进入了保护模式

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

该警告是触发了Eureka Server的自我保护机制。

Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果低于,就会将当前实例注册信息保护起来,让实例不会过期,尽可能保护这些注册信息。

但是如果在保护期间,实例出现问题,那么客户端很容易拿到实际已经不存在的服务实例,会出现调用失败。这个时候客户端的容错机制就很重要了。(重新请求,断路器)注册中心都有一个容错机制!Zookeeper

保护机制,可能会导致服务实例不能够被正确剔除。

在本地开发时,可使用:eureka.server.enable-self-preservation=false关闭保护机制,使不可用实例能够正常下线。

2.搭建Eureka服务器

官方文档:http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#spring-cloud-eureka-server

2.1父工程中定义Spring Cloud的版本


<dependencyManagement>

    <dependencies>

        

        <!--Spring Cloud-->

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-dependencies</artifactId>

            <version>Finchley.SR2</version>

            <type>pom</type>

            <scope>import</scope>

        </dependency>

    </dependencies>

</dependencyManagement>

2.2 创建Eureka服务器模块

创建Maven模块:test_eureka

2.3 配置pom

<dependencies>
    <!--注册中心-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>

2.4 配置application.properties

#服务端口
server.port=8004

#是否将自己注册到Eureka服务器中,本身是服务器,无需注册
eureka.client.register-with-eureka=false
#是否从Eureka中获取注册信息
eureka.client.fetch-registry=false
#Eureka客户端与Eureka服务端进行通信的地址
eureka.client.service-url.defaultZone=http://127.0.0.1:${server.port}/eureka/

2.5 创建启动类

在启动类上添加注解@EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

2.6 logback.xml

2.7 启动Eureka注册中心并在浏览器中访问

http://localhost:8004

System Status:系统信息

DS Replicas:服务器副本
Instances currently registered with Eureka:已注册的微服务列表

General Info:一般信息 

Instance Info:实例信息 

2.8 健康监控的路由

http://localhost:8004/actuator

http://localhost:8004/actuator/health

3.服务注册

在test_teacher、test_vod微服务中完成以下配置

3.1 客户端配置pom

配置Eureka客户端的pom依赖

<!--服务注册-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

3.2 添加服务配置信息

配置application.properties,在客户端微服务中添加注册Eureka服务的配置信息

#指定注册中心地址
eureka.client.service-url.defaultZone=http://127.0.0.1:8004/eureka/
#eureka服务器上获取的是服务器的ip地址,否则是主机名
eureka.instance.prefer-ip-address=true

3.3 添加Eureka客户端注解

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

@EnableEurekaClient

3.4 启动客户端微服务

启动注册中心

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

4. Feign

4.1 基本概念

  • 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时自行封装服务调用客户端的开发量。

4.2 实现服务调用

4.2.1 需求:课程大纲中删除小节

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

4.2.2 在调用端添加pom依赖

在test_teacher微服务中添加

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

4.2.3 在调用端的启动类添加注解

@EnableFeignClients

4.2.4 创建包和接口

创建client包

@FeignClient注解用于指定从哪个服务中调用功能 ,名称与被调用的服务名保持一致。

@GetMapping注解用于对被调用的微服务进行地址映射。

@PathVariable注解一定要指定参数名称,否则出错

@Component注解防止,在其他位置注入CodClient时idea报错


package com.test.edu.client;

​

@FeignClient("test-vod")

@Component

public interface VodClient {

    @DeleteMapping(value = "/video/{videoId}")

    public R removeVideo(@PathVariable("videoId") String videoId);

}

5、调用微服务

在调用端的VideoServiceImpl中调用client中的方法

@Override
public Boolean removeVideoById(String id) {

    // TODO 删除阿里云上的视频
    //查询云端视频id
    EduVideo video = baseMapper.selectById(id);
    String videoSourceId = video.getVideoSourceId();
    //删除视频资源
    if(!StringUtils.isEmpty(videoSourceId)){
        vodClient.removeVideo(videoSourceId);
    }

    // 删除数据库中的Video
    int delete = baseMapper.deleteById(id);

    return delete == 1;
}

4.2.5 测试

启动相关微服务

测试删除课时的功能

5.删除业务

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

5.1 vod服务

5.1.1 业务接口:VodService.java

/**
 * 根据多个视频ID删除云端视频
 * @param videoIdList
 */
void removeVideoList(List<String> videoIdList);

5.1.2 业务实现:VodServiceImpl.java

@Override
public void removeVideoList(List<String> videoIdList) {
    try {
        //初始化
        DefaultAcsClient client = AliyunVodSDKUtil.initVodClient(
                ConstantPropertiesUtil.ACCESS_KEY_ID,
                ConstantPropertiesUtil.ACCESS_KEY_SECRET);

        //创建请求对象
        //一次只能批量删20个
        String str = org.apache.commons.lang.StringUtils.join(videoIdList.toArray(), ",");
        DeleteVideoRequest request = new DeleteVideoRequest();
        request.setVideoIds(str);

        //获取响应
        DeleteVideoResponse response = client.getAcsResponse(request);

        System.out.print("RequestId = " + response.getRequestId() + "\n");

    } catch (ClientException e) {
        //throw new EduException(20001, "视频删除失败");
    }
}

5.1.3 Controller层接口

controller:VodController.java

/**
 * 批量删除视频
 * @param videoIdList
 * @return
 */
@DeleteMapping("deleteIds")
public Result removeVideoList(
        @ApiParam(name = "videoIdList", value = "云端视频id", required = true)
        @RequestParam("videoIdList") List videoIdList){

    vodService.removeVideoList(videoIdList);
    return Result.ok().message("视频删除成功");
}

5.1.4 Swagger测试

输入多个id,每个一行

5.2 test_teacher服务

5.2.1 VodClient调用接口

VodClient.java

@DeleteMapping(value = "/vod/deleteIds")
public Result removeVideoList(@RequestParam("videoIdList") List videoIdList);

5.2.2 VideoService接口:

/**
 * 根据课程ID删除小节视频
 * @param courseId
 * @return
 */
boolean removeByCourseId(String courseId);

5.2.3 VideoServiceImpl实现

VideoServiceImpl.java

@Override
public boolean removeByCourseId(String courseId) {

    //根据课程id查询所有视频列表
    QueryWrapper<EduVideo> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("course_id", courseId);
    queryWrapper.select("video_source_id");
    List<EduVideo> videoList = baseMapper.selectList(queryWrapper);

    //得到所有视频列表的云端原始视频id
    List<String> videoSourceIdList = new ArrayList<>();
    for (int i = 0; i < videoList.size(); i++) {
        EduVideo video = videoList.get(i);
        String videoSourceId = video.getVideoSourceId();
        if(!StringUtils.isEmpty(videoSourceId)){
            videoSourceIdList.add(videoSourceId);
        }
    }

    //调用vod服务删除远程视频
    if(videoSourceIdList.size() > 0){
        vodClient.removeVideoList(videoSourceIdList);
    }

    //删除video表示的记录
    QueryWrapper<EduVideo> queryWrapper2 = new QueryWrapper<>();
    queryWrapper2.eq("course_id", courseId);
    Integer count = baseMapper.delete(queryWrapper2);
    return null != count && count > 0;
}

5.2.4 CourseServiceImpl.java

@Override
public Boolean deleteById(String id) {
    
    //根据id删除所有视频
    videoService.removeByCourseId(id);

    //根据id删除所有章节
    chapterService.removeByCourseId(id);

    // 删除描述
    boolean b = courseDescriptionService.removeById(id);
    if(!b){
        return false;
    }
    // 删除基本信息
    int i = baseMapper.deleteById(id);
    return i == 1;
}

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐