SpringCloud(一)

1. 系统架构的演变

软件架构的发展经历了从单体架构、分布式架构、SOA架构到微服务架构的过程。

1.1 单体架构

Web 应用程序发展的早期,大部分 web 工程师将所有的功能模块打包到一起并放在一个 web 容器中运行,所有功能模块使用同一个数据库。

下图是一个单体架构的电商系统:

在这里插入图片描述

特点:

  1. 所有的功能集成在一个项目工程中。
  2. 所有的功能打在一个 war 包部署到服务器。
  3. 通过部署应用集群和数据库集群来提高系统的性能。

优点:

  1. 项目架构简单,前期开发成本低,周期短,小型项目的首选。
  2. 开发效率高,模块之间交互采用本地方法调用。
  3. 容易部署,运维成本小,直接打包为一个完整的包,拷贝到 web 容器的某个目录下即可运行。
  4. 容易测试:IDE 都是为开发单个应用设计的、容易测试,在本地就可以启动完整的系统。

缺点:

  1. 全部功能集成在一个工程中,对于大型项目不易开发、扩展及维护。
  2. 版本迭代速度逐渐变慢,修改一个地方就要将整个应用全部编译、部署、启动,开发及测试周期过长。
  3. 无法按需伸缩,通过集群的方式来实现水平扩展,无法针对某业务按需伸缩。

1.2 分布式架构

针对单体架构的不足,为了适应大型项目的开发需求,许多公司将一个单体系统按业务垂直拆分为若干系统,系统之间通过网络交互来完成用户的业务处理,每个系统可分布式部署,这种架构称为分布式架构。

在这里插入图片描述

特点:

  1. 按业务垂直拆分成一个一个的单体系统,此架构也称为垂直架构。
  2. 系统与系统之间的存在数据冗余,耦合性较大,如上图中三个项目都存在客户信息。
  3. 系统之间的接口多为实现数据同步,如上图中三个项目要同步客户信息。

优点:

  1. 通过垂直拆分,每个子系统变成小型系统,功能简单,前期开发成本低,周期短。
  2. 每个子系统可按需伸缩。
  3. 每个子系统可采用不同的技术。

缺点:

  1. 子系统之间存在数据冗余、功能冗余,耦合性高。
  2. 按需伸缩粒度不够,对同一个子系统中的不同的业务无法实现,比如订单管理和用户管理。

1.3 SOA 架构

SOA 是一种面向服务的架构,基于分布式架构,它将不同业务功能按服务进行拆分,并通过这些服务之间定义良好的接口和协议联系起来。

在这里插入图片描述

特点:

  1. 基于 SOA 的架构思想,将重复公用的功能抽取为组件,以服务的方式向各各系统提供服务。
  2. 各各系统与服务之间采用 webservice、rpc 等方式进行通信。
  3. ESB 企业服务总线作为系统与服务之间通信的桥梁。

优点:

  1. 将重复的功能抽取为服务,提高开发效率,提高系统的可重用性、可维护性。
  2. 可以针对不同服务的特点按需伸缩。
  3. 采用 ESB 减少系统中的接口耦合。

缺点:

  1. 系统与服务的界限模糊,会导致抽取的服务的粒度过大,系统与服务之间耦合性高。
  2. 虽然使用了 ESB,但是服务的接口协议不固定,种类繁多,不利于系统维护。

1.4 微服务架构

基于 SOA 架构的思想,为了满足移动互联网对大型项目及多客户端的需求,对服务层进行细粒度的拆分,所拆分的每个服务只完成某个特定的业务功能,比如订单服务只实现订单相关的业务,用户服务实现用户管理相关的业务等等,服务的粒度很小,所以称为微服务架构。

在这里插入图片描述

特点:

  1. 服务层按业务拆分为一个一个的微服务。
  2. 微服务的职责单一。
  3. 微服务之间采用 RESTful、RPC 等轻量级协议传输。
  4. 有利于采用前后端分离架构。

优点:

  1. 服务拆分粒度更细,有利于资源重复利用,提高开发效率。
  2. 可以更加精准的制定每个服务的优化方案,按需伸缩。
  3. 适用于互联网时代,产品迭代周期更短。

缺点:

  1. 开发的复杂性增加,因为一个业务流程需要多个微服务通过网络交互来完成。
  2. 微服务过多,服务治理成本高,不利于系统维护。

以上内容参考:http://www.pbteach.com/article/framework/framework_evolution/

2. 远程服务调用

2.1 RPC 和 HTTP

无论是 SOA 还是微服务,都面临着远程服务调用。那么远程服务调用方式有哪些呢?

常见的远程服务调用方式有以下 2 种:

  • RPC:远程过程调用协议,类似的还有 RMI。基于原生 TCP 通信,自定义数据格式,速度快,效率高。早期的 Webservice,现在热门的 Dubbo,都是 RPC 的典型代表。
  • HTTP:HTTP 其实是一种网络传输协议,基于 TCP 协议,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用 HTTP 协议,也可以用来进行远程服务调用。缺点是消息封装臃肿,优点是对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。现在的 SpringCloud,就是 HTTP 的典型代表。

2.2 RestTemplate

Spring 提供了一个 RestTemplate 模板工具类,对基于 HTTP 的客户端进行了封装,并且实现了对象与 JSON 的序列化和反序列化,非常方便。

下面介绍 RestTemplate 的使用:

  1. 创建 RestTemplate 配置类,注册一个 RestTemplate 对象

    @Configuration
    public class RestTemplateConfiguration {
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
  2. 在测试类中注入 RestTemplate 对象,并通过 RestTemplate 的 getForObject() 方法将得到的 JSON 字符串反序列化为 User 对象

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = Demo2Application.class)
    public class UserTest {
        @Autowired
        private RestTemplate restTemplate;
    
        @Test
        public void testGet() {
            User user = restTemplate.getForObject("http://localhost/user/findById/52", User.class);
            System.out.println(user);
        }
    }
    

3. SpringCloud 简介

SpringCloud 是一套完整的微服务解决方案,基于 SpringBoot 框架。SpringCloud 将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。其主要涉及的组件包括:

  • Eureka:服务治理组件,包含服务注册中心,服务注册与发现机制的实现。(服务治理,服务注册/发现)
  • Zuul:网关组件,提供智能路由,访问过滤功能
  • Ribbon:客户端负载均衡的服务调用组件(客户端负载)
  • Feign:服务调用,给予Ribbon和Hystrix的声明式服务调用组件 (声明式服务调用)
  • Hystrix:容错管理组件,实现断路器模式,帮助服务依赖中出现的延迟和为故障提供强大的容错能力。(熔断、断路器,容错)

架构图:

在这里插入图片描述

4. 微服务场景模拟

我们模拟一个服务调用的场景,搭建两个工程:zt-service-provider(服务提供方)和 zt-service-customer(服务调用方)。

服务提供方:使用 Mybatis 操作数据库,实现对数据的增删改查,并对外提供 Rest 接口服务。

服务消费方:使用 RestTemplate 远程调用服务提供方的 Rest 接口服务,获取数据。

4.1 服务提供方

4.1.1 创建数据库

DROP TABLE IF EXISTS `table_user`;
CREATE TABLE `table_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(20) DEFAULT NULL,
  `user_gender` char(2) DEFAULT '未知',
  `user_address` varchar(30) DEFAULT NULL,
  `user_birthday` date DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;

INSERT INTO `table_user` VALUES ('1', '张三', '男', '北京', '2020-01-15');
INSERT INTO `table_user` VALUES ('2', '李四', '男', '上海', '2020-01-14');
INSERT INTO `table_user` VALUES ('3', '王五', '女', '广州', '2020-01-12');

4.1.2 创建工程

  1. 打开 IDEA

  2. Spring Initializr --> Next

  3. 填写项目信息 --> Next

    在这里插入图片描述

  4. 添加依赖 --> Next

    在这里插入图片描述

  5. 填写项目位置 --> Finish

    在这里插入图片描述

4.1.3 编写代码

  1. 引入通用 mapper 依赖

    <!--通用 Mapper-->
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>2.1.5</version>
    </dependency>
    
  2. 编写配置文件 application.yaml

    server:
      port: 8081
    
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mapper?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
    
  3. 修改引导类,在类上添加 @MapperScan 注解

    @SpringBootApplication
    @MapperScan("com.zt.mapper")
    public class ZtServiceProviderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZtServiceProviderApplication.class, args);
        }
    
    }
    
    
  4. 创建实体类

    package com.zt.pojo;
    
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import java.io.Serializable;
    import java.util.Date;
    
    @Table(name = "table_user")
    public class User implements Serializable {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Integer userId;
        private String userName;
        private String userGender;
        private String userAddress;
        private Date userBirthday;
    
        public User() {
        }
    
        public User(String userName, String userGender, String userAddress, Date userBirthday) {
            this.userName = userName;
            this.userGender = userGender;
            this.userAddress = userAddress;
            this.userBirthday = userBirthday;
        }
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getUserGender() {
            return userGender;
        }
    
        public void setUserGender(String userGender) {
            this.userGender = userGender;
        }
    
        public String getUserAddress() {
            return userAddress;
        }
    
        public void setUserAddress(String userAddress) {
            this.userAddress = userAddress;
        }
    
        public Date getUserBirthday() {
            return userBirthday;
        }
    
        public void setUserBirthday(Date userBirthday) {
            this.userBirthday = userBirthday;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "userId=" + userId +
                    ", userName='" + userName + '\'' +
                    ", userGender='" + userGender + '\'' +
                    ", userAddress='" + userAddress + '\'' +
                    ", userBirthday=" + userBirthday +
                    '}';
        }
    }
    
    
  5. 创建持久层

    public interface UserMapper extends Mapper<User> {
    }
    
  6. 创建业务层

    @Service
    public class UserService {
        @Autowired
        private UserMapper userMapper;
    
        public User queryUserById(Integer id) {
            return userMapper.selectByPrimaryKey(id);
        }
    }
    
  7. 创建控制层

    @RestController
    @RequestMapping("user")
    public class UserController {
        @Autowired
        private UserService userService;
    
        @GetMapping("{id}")
        public User queryUserById(@PathVariable("id") Integer id) {
            return userService.queryUserById(id);
        }
    }
    
    

4.1.4 启动并测试

  1. 启动项目

  2. 打开浏览器,访问下面的地址

    http://localhost:8081/user/1
    

    在这里插入图片描述

4.2 服务消费方

4.2.1 创建工程

  1. 打开 IDEA

  2. Spring Initializr --> Next

  3. 填写项目信息 --> Next

    在这里插入图片描述

  4. 添加依赖 --> Next

    在这里插入图片描述

  5. 填写项目位置 --> Finish

    在这里插入图片描述

4.2.2 编写代码

  1. 编写配置文件 application.yaml

    server:
      port: 8082
    
  2. 修改引导类,创建一个 RestTemplate 的 Bean 对象

    @SpringBootApplication
    public class ZtServiceCustomerApplication {
    
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
        
        public static void main(String[] args) {
            SpringApplication.run(ZtServiceCustomerApplication.class, args);
        }
    
    }
    
  3. 创建实体类

    public class User implements Serializable {
        private Integer userId;
        private String userName;
        private String userGender;
        private String userAddress;
        private Date userBirthday;
    
        public User() {
        }
    
        public User(String userName, String userGender, String userAddress, Date userBirthday) {
            this.userName = userName;
            this.userGender = userGender;
            this.userAddress = userAddress;
            this.userBirthday = userBirthday;
        }
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getUserGender() {
            return userGender;
        }
    
        public void setUserGender(String userGender) {
            this.userGender = userGender;
        }
    
        public String getUserAddress() {
            return userAddress;
        }
    
        public void setUserAddress(String userAddress) {
            this.userAddress = userAddress;
        }
    
        public Date getUserBirthday() {
            return userBirthday;
        }
    
        public void setUserBirthday(Date userBirthday) {
            this.userBirthday = userBirthday;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "userId=" + userId +
                    ", userName='" + userName + '\'' +
                    ", userGender='" + userGender + '\'' +
                    ", userAddress='" + userAddress + '\'' +
                    ", userBirthday=" + userBirthday +
                    '}';
        }
    }
    
    
  4. 创建控制层

    @Controller
    @RequestMapping("user")
    public class UserController {
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping
        @ResponseBody
        public User queryUserById(@RequestParam("id") Integer id) {
            return restTemplate.getForObject("http://localhost:8081/user/"+id,User.class);
        }
    }
    
    

4.2.3 启动并测试

  1. 启动服务提供方项目

  2. 启动服务消费方项目

  3. 打开浏览器,访问下面的地址

    http://localhost:8082/user?id=1
    

    在这里插入图片描述

4.3 存在的问题

  1. 简单回顾一下,刚才我们写了什么

    • 服务提供方:使用 Mybatis 操作数据库,实现对数据的增删改查,并对外提供 Rest 接口服务。
    • 服务消费方:使用 RestTemplate 远程调用服务提供方的 Rest 接口服务,获取数据。
  2. 存在的问题

    • 在 customer 中,我们把 url 地址硬编码到了代码中,不方便后期维护
    • customer 需要记忆 provider 的地址,如果出现变更,可能得不到通知,地址将失效
    • customer 不清楚 provider 的状态,服务宕机也不知道
    • provider 只有 1 台服务,不具备高可用性
    • 即便 provider 形成集群,consumer 还需自己实现负载均衡
  3. 其实上面说的问题,概括一下就是分布式服务必然要面临的问题

    • 服务管理
      • 如何自动注册和发现
      • 如何实现状态监管
      • 如何实现动态路由
    • 服务如何实现负载均衡
    • 服务如何解决容灾问题
    • 服务如何实现统一配置
  4. 其实上面的问题在 SpringCloud 中都得到了解决,接下来看看 SpringCloud 是如何解决的

5. Eureka 服务注册中心

5.1 Eureka 的概念

Eureka 是一个服务治理组件,它实现了服务的自动注册、发现、状态监控。

它主要包括两个组件:Eureka Server 和 Eureka Client

  • Eureka Server 提供服务注册和发现的功能,也就是微服务中的注册中心。
  • Eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互,也就是微服务中的客户端和服务端。

每个微服务启动时,都会通过 Eureka Client 向 Eureka Server 注册自己,Eureka Server 会存储该服务的信息。

5.2 Eureka 的作用

Eureka 负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉 Eureka,然后 Eureka 会把符合你需求的服务告诉你。

同时,服务提供方与 Eureka 之间通过 “心跳” 机制进行监控,当某个服务提供方出现问题,Eureka 自然会把它从服务列表中剔除。

5.3 Eureka 原理图

在这里插入图片描述

  • Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
  • 提供者:启动后向 Eureka 注册自己信息(地址,提供什么服务)
  • 消费者:向 Eureka 订阅服务,Eureka 会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
  • 心跳(续约):提供者定期通过 http 方式向 Eureka 刷新自己的状态

5.4 搭建 Eureka Server

5.4.1 创建工程

  1. 打开 IDEA

  2. Spring Initializr --> Next

  3. 填写项目信息 --> Next

    在这里插入图片描述

  4. 添加依赖 --> Next

    在这里插入图片描述

  5. 填写项目位置 --> Finish

    在这里插入图片描述

5.4.2 Eureka Server 的配置

  1. 修改引导类,在类上添加 @EnableEurekaServer 注解

    @SpringBootApplication
    @EnableEurekaServer
    public class ZtEurekaApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZtEurekaApplication.class, args);
        }
    
    }
    
    
  2. 编写配置文件 application.yaml

    # 服务端口号
    server:
      port: 10086
    # 服务名称
    spring:
      application:
        name: eureka-server
    
    # Eureka 相关配置
    eureka:
      client:
        # Eureka 默认的服务地址空间信息配置
        service-url:
          defaultZone: http://localhost:${server.port}/eureka
        # 是否从其他的服务中心同步服务列表
        fetch-registry: false
        # 是否把自己作为服务注册到其他服务注册中心
        register-with-eureka: false
    

5.4.3 启动并测试

  1. 启动项目

  2. 打开浏览器,访问以下地址

    http://localhost:10086/
    
  3. 成功访问 Eureka Server 管理界面

    在这里插入图片描述

5.5 注册服务到 Eureka

5.5.1 注册服务提供方

  1. 打开 zt-service-provider 工程

  2. 在 pom.xml 中,添加 SpringCloud 的相关依赖

    1. 参照 Eureka Server,添加 SpringCloud 依赖

      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-dependencies</artifactId>
                  <version>Hoxton.SR1</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
      
    2. 添加 Eureka 客户端依赖

      <!-- Eureka客户端 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      
  3. 修改配置文件 application.yaml

    server:
      port: 8081
    
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mapper?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
      application:
        name: service-provider
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka
    
  4. 修改引导类,在类上添加 @EnableDiscoveryClient 注解来开启 Eureka 客户端功能

    @SpringBootApplication
    @MapperScan("com.zt.mapper")
    @EnableDiscoveryClient
    public class ZtServiceProviderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZtServiceProviderApplication.class, args);
        }
    
    }
    
  5. 启动 zt-service-provider 项目

  6. 打开浏览器,访问以下地址

    http://localhost:10086/
    
  7. 发现成功注册服务提供方

    在这里插入图片描述

5.5.2 注册服务消费方

和上面的注册服务提供方是一样的流程,这里就不写了,注册结果如下:

在这里插入图片描述

5.6 优化服务消费方

前面说过服务消费方中,还存在 url 硬编码问题,接下来用 Eureka 来解决这个问题

  1. 修改 UserController

    @Controller
    @RequestMapping("user")
    public class UserController {
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private DiscoveryClient discoveryClient; // Eureka客户端,可以获取到 Eureka 中服务的信息
    
        @GetMapping
        @ResponseBody
        public User queryUserById(@RequestParam("id") Integer id) {
            List<ServiceInstance> instances = discoveryClient.getInstances("service-provider"); // 根据服务名称,获取服务实例
            ServiceInstance serviceInstance = instances.get(0); // 获取第一个实例
            return restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/" + id, User.class);
        }
    }
    
    
  2. 重新启动服务消费方项目

  3. 打开浏览器,访问下面的地址

    http://localhost:8082/user?id=2
    

    在这里插入图片描述

5.7 高可用 Eureka Server

Eureka Server 即服务的注册中心,在刚才的案例中,我们只有一个 Eureka Server,事实上 Eureka Server 也可以是一个集群,这样就形成了高可用 Eureka 注册中心

5.7.1 服务同步

多个 Eureka Server 之间也会互相注册为服务,当服务提供者注册到 Eureka Server 集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到 Eureka Server 集群中的任意一个节点,都可以获取到完整的服务列表信息。

5.7.2 搭建高可用的 Eureka Server

我们假设要运行两个 Eureka Server 的集群,端口分别为:10086 和 10087。只需要把 zt-eureka 这个项目启动两次即可。

  1. 修改 zt-eureka 的配置文件 application.yaml

    # 服务端口号
    server:
      port: 10086
    # 服务名称
    spring:
      application:
        name: eureka-server
    
    # Eureka 相关配置
    eureka:
      client:
        # Eureka 默认的服务地址空间信息配置
        service-url:
          defaultZone: http://localhost:10087/eureka # 配置其他 Eureka 服务的地址
    
  2. 启动第一个 zt-eureka 项目

    注意:此时,会肯定会报错,因为会找不到 10087 这个节点。不用管这个错误,继续下去即可。

  3. 再次修改 zt-eureka 的配置文件 application.yaml

    # 服务端口号
    server:
      port: 10087
    # 服务名称
    spring:
      application:
        name: eureka-server
    
    # Eureka 相关配置
    eureka:
      client:
        # Eureka 默认的服务地址空间信息配置
        service-url:
          defaultZone: http://localhost:10086/eureka # 配置其他 Eureka 服务的地址
    
  4. 再次启动第二个 zt-eureka 项目

    IDEA 中一个应用不能启动两次,因此我们需要重新配置一个启动器

    在这里插入图片描述

    在这里插入图片描述

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NfQjm0wY-1579697903590)(C:\Users\admin\Desktop\常用\乐优商城笔记\day02-springcloud\创建启动器-2.png)]

  5. 访问集群

    在这里插入图片描述
    在这里插入图片描述

5.8 Eureka 详解

5.8.1 服务提供者

服务注册

服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-eureka=true参数,如果值确实为 true,则会向 EurekaServer 发起一个 Rest 请求,并携带自己的元数据信息,Eureka Server 会把这些信息保存到一个双层 Map 结构中。

  • 第一层 Map 的 Key 就是服务 id,一般是配置中的spring.application.name属性
  • 第二层 Map 的 key 是服务的实例 id。一般 host+ serviceId + port,例如:locahost:service-provider:8081
  • 值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群。

服务续约

在注册服务完成以后,服务提供者会维持一个心跳(定时向 EurekaServer 发起 Rest 请求),告诉 EurekaServer “我还活着”,我们又称为服务的续约(renew)。

有两个重要参数可以修改服务续约的行为:

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30
  • lease-renewal-interval-in-seconds:服务续约(renew)的间隔,默认为 30 秒
  • lease-expiration-duration-in-seconds:服务失效时间,默认值 90 秒

也就是说,默认情况下每个 30 秒服务会向注册中心发送一次心跳,证明自己还活着。如果超过 90 秒没有发送心跳,EurekaServer 就会认为该服务宕机,会从服务列表中移除,这两个值在生产环境不要修改,默认即可。

5.8.2 服务消费者

获取服务列表

当服务消费者启动时,会检测eureka.client.fetch-registry=true参数,如果值为 true,则会拉取 Eureka Server 服务的列表只读备份,然后缓存在本地。并且每隔 30 秒会重新获取并更新数据。我们可以通过下面的参数来修改:

eureka:
  client:
    registry-fetch-interval-seconds: 30

生产环境中,我们不需要修改这个值,默认即可。

5.8.3 失效剔除和自我保护

服务下线

当服务进行正常关闭操作时,它会触发一个服务下线的 REST 请求给 Eureka Server,告诉服务注册中心:“我要下线了”。服务中心接受到请求之后,将该服务置为下线状态。

失效剔除

有些时候,我们的服务提供方并不一定会正常下线,可能因为内存溢出、网络故障等原因导致服务无法正常工作。Eureka Server 需要将这样的服务剔除出服务列表,因此它会开启一个定时任务,每隔 60 秒对所有失效的服务(超过90秒未响应)进行剔除。

可以通过eureka.server.eviction-interval-timer-in-ms参数对其进行修改,单位是毫秒,生产环境不要修改。

eureka:
  server:
    eviction-interval-timer-in-ms: 60000

自我保护

我们关停一个服务,就会在Eureka面板看到一条警告:

在这里插入图片描述

这是触发了 Eureka 的自我保护机制。当一个服务未按时进行心跳续约时,Eureka 会统计最近 15 分钟心跳失败的服务实例的比例是否超过了 85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka 就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。

但是这给我们的开发带来了麻烦, 因此开发阶段我们都会关闭自我保护模式:

eureka:
  server:
    enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值