服务注册与发现

2023-12-16 16:32:09

本篇主要介绍的是Spring Cloud Eureka。

1.1 简介

Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件的一部分,基于 Netflix Eureka 做了二次封装,主要负责完成微服务架构中的服务治理功能,服务治理可以说是微服务架构中最为核心和基础的模块,他主要用来实现各个微服务实例的自动化注册与发现。

  • 服务注册
    在服务治理框架中,通常都会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务,将主机与端口号、版本号、通信协议等一些附加信息告知注册中心,注册中心按照服务名分类组织服务清单,服务注册中心还需要以心跳的方式去监控清单中的服务是否可用,若不可用需要从服务清单中剔除,达到排除故障服务的效果。
  • 服务发现
    由于在服务治理框架下运行,服务间的调用不再通过指定具体的实例地址来实现,而是通过向服务名发起请求调用实现。

Spring Cloud Eureka 使用 Netflix Eureka 来实现服务注册与发现,即包括了服务端组件,也包含了客户端组件,并且服务端和客户端均采用 Java 编写,所以 Eureka 主要适用与通过 Java 实现的分布式系统,或是与 JVM 兼容语言构建的系统,但是,由于 Eureka 服务端的服务治理机制提供了完备的 RESTful API,所以他也支持将非 Java 语言构建的微服务纳入 Eureka 的服务治理体系中来。

1.2 项目工程搭建
1.2.1 创建父工程

创建一个Maven工程,配置相关依赖:

    <?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">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.giser.springcloud</groupId>
        <artifactId>giser-springcloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <packaging>pom</packaging>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.5.RELEASE</version>
            <relativePath/>
        </parent>
    
        <properties>
            <java.version>1.8</java.version>
            <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
            <mapper.starter.version>2.1.5</mapper.starter.version>
            <mysql.version>5.1.46</mysql.version>
        </properties>
    
        <dependencyManagement>
            <dependencies>
                <!-- springCloud -->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!-- 通用Mapper启动器 -->
                <dependency>
                    <groupId>tk.mybatis</groupId>
                    <artifactId>mapper-spring-boot-starter</artifactId>
                    <version>${mapper.starter.version}</version>
                </dependency>
                <!-- mysql驱动 -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>${mysql.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-config</artifactId>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
1.2.2 创建子模块-EurekaServer

创建Eureka Server注册中心子模块

    <?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>giser-springcloud</artifactId>
            <groupId>com.giser.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>eureka-server</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
                <version>2.5.5</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
            </dependency>
        </dependencies>
    
    </project>

此模块为Eureka Server端,主启动类如下:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer //  声明当前服务为Eureka服务
    public class EurekaServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class, args);
        }
    }

	服务配置信息:

    server:
      port: 10086
    spring:
      application:
        name: eureka-server
    eureka:
      client:
        service-url:
          # eureka 服务地址,如果是集群的话;需要指定其它集群eureka地址
          defaultZone: http://127.0.0.1:10086/eureka
        # 不注册自己
        register-with-eureka: false
        # 不拉取服务
        fetch-registry: false

说明:

- eureka.client.registerWithEureka:表示是否将自己注册到Eureka Server,默认为true。由于当前应用就是Eureka Server,故而设置为false。
- eureka.client.fetchRegistry:表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设置为false。
- eureka.client.serviceUrl.defalseZone:设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址,多个地址可用逗号(英文的)分割。

此时启动服务可以访问localhost:10086看到注册中心界面,正常打开以上网页,说明Eureka注册中心配置成功。

注意:默认端口为8761。
1.2.3 创建子模块-EurekaClient
创建Eureka Client注册中心子模块
    <?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>giser-springcloud</artifactId>
            <groupId>com.giser.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>eureka-client</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
            </dependency>
        </dependencies>
    
    </project>

此模块为Eureka Client端,主启动类如下:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class EurekaClientApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaClientApplication.class, args);
        }
    }

服务配置信息:

    spring:
      application:
        name: eureka-client
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:10086/eureka

启动项目,在“Instances currently registered with Eureka”列表中可以看到注册的服务信息,客户端注册成功。

1.2.4 为Eureka添加安全认证

默认情况下我们就直接直接访问到eureka的界面了。如果不想让所有人都能访问到eureka的界面,可以加上权限认证,输入账号密码才能访问。

服务端:

Eureka Server中pom文件添加spring-boot-starter-security依赖

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

yml文件内容修改为

    server:
      port: 10086
    spring:
      application:
        name: eureka-server
      security:
        user:
          name: admin
          password: 123
    eureka:
      instance:
        hostname: localhost
      client:
        service-url:
          # eureka 服务地址,如果是集群的话;需要指定其它集群eureka地址
    #      defaultZone: http://127.0.0.1:10086/eureka
          defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
        # 不注册自己
        register-with-eureka: false
        # 不拉取服务
        fetch-registry: false

启动项目,访问地址:http://localhost:10086/,会提示我们输入用户名和密码

客户端:

application.yml配置文件内容修改:

    eureka:
      client:
        serviceUrl:
          defaultZone: http://admin:123456@localhost:8761/eureka/

在地址里加上用户名密码,然后运行还是报错无法注册到eureka,是因为新版本的security默认开启csrf了,关掉就好了

服务端Eureka Server项目中新建一个配置类,如下

    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable();  //  关闭csrf
            http.authorizeRequests().anyRequest().authenticated().and().httpBasic();//  开启认证
        }
    }

重新启动Eureka Server,访问localhost:10086,出现不一样的登录提示。

输入用户名和密码登录后,启动Eureka Client项目,此时再刷新eureka页面,发现已经成功注册!

1.3 服务调用
1.3.1 简介

无论是微服务,还是SOA,都面临着服务的远程调用。服务间常见的远程调用方式有以下两种:

  • RPC
    Remote Produce Call,远程过程调用,RPC是基于Socket,工作在会话层。可以自定义数据格式,速度快,效率高。典型的有WebService和Dubbo。

  • Http
    http实际上是一种网络传输协议,基于TCP,工作在应用层。规定了数据传输的格式。缺点是消息封装臃肿,优势是对服务提供和调用方没有任何技术限定。

  • 区别:
    RPC是根据语言的API来定义的,而不是根据王略的应用来定义的。
    如果项目全部采用Java技术栈,则使用Dubbo作为微服务架构是一个不错的选择。
    如果项目的技术栈多样化,则使用SpringCloud搭建微服务更好,此时会使用Http方式来实现服务间的调用。

  • http客户端
    开源的http客户端有很多,如HttpClient、OKHttp、URLConnection等,而Spring也对http客户端进行了封装,提供了RestTemplate工具类。
    RestTemplate模板工具类也实现了对象与json的序列化和反序列化。

1.3.2 使用RestTemplate

启动类中注入RestTemplate

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableDiscoveryClient //开启Eureka客户端发现功能
    public class ConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }

使用RestTemplate

    @RestController
    @RequestMapping("/consumer")
    public class ConsumerController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/{id}")
        public String queryById(@PathVariable Long id){
            return restTemplate.getForObject("http://user-service/user/" + id, String.class);
        }
    
    }
1.4 服务提供者注册服务到EurekaServer

创建服务提供者user-service模块,

    <?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>giser-springcloud</artifactId>
            <groupId>com.giser.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>user-service</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- 通用Mapper启动器 -->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
            </dependency>
            <!-- mysql驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        </dependencies>
    
    </project>
配置yml
    server:
      port: 9091
    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/book
        username: root
        password: root
      application:
        name: user-service # 应用名,作为服务的ID
      security: # 安全认证信息
        user:
          name: admin
          password: 123
    
    mybatis:
      type-aliases-package: com.giser.user.pojo
    
    eureka:
      client:
        service-url:
          defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@localhost:10086/eureka

	启动类,通过@EnableDiscoveryClient注解进行服务发现注册

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import tk.mybatis.spring.annotation.MapperScan;
    
    @SpringBootApplication
    @MapperScan("com.giser.user.mapper")
    @EnableDiscoveryClient
    public class UserApplication {
        public static void main(String[] args) {
            SpringApplication.run(UserApplication.class, args);
        }
    }
1.5 服务消费者进行服务调用

服务消费者通过服务名进行服务调用。创建服务消费者service-consumer模块,配置如下:

    <?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>giser-springcloud</artifactId>
            <groupId>com.giser.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>consumer-demo</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-autoconfigure</artifactId>
            </dependency>
        </dependencies>
    
    </project>

配置信息

    server:
      port: 8081
    spring:
      application:
        name: service-consumer
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:10086/eureka

启动类,开启服务发现@EnableDiscoveryClient

   import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableDiscoveryClient //开启Eureka客户端发现功能
    public class ConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }

控制器类

    import com.giser.consumer.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/consumer")
    public class ConsumerController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private DiscoveryClient discoveryClient;
    
        @GetMapping("/{id}")
        public User queryById(@PathVariable Long id){
            /**
             * 利用服务发现,通过服务名获取Eureka注册中心的服务实例列表
             */
            List<ServiceInstance> serviceList = discoveryClient.getInstances("user-service");
            //  取出其中一个服务实例
            ServiceInstance serviceInstance = null;
            if (serviceList != null && serviceList.size() > 0){
                serviceInstance = serviceList.get(0);
            }
    
            String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/" + id;
    
            return restTemplate.getForObject(url, User.class);
        }
    
    }

此时启动EurekaServer、服务提供者和消费者,访问http://localhost:8080/consumer/8,可以看到拼接后的请求地址。

1.6 Eureka高可用配置

Eureka Server是一个Web应用,可以启动多个实例,每个实例配置不同的端口,保证Eureka Server的高可用。将Eureka Server作为一个服务注册到其它的Eureka Server中,这样多个Eureka Server之间就能够相互发现对方,实现服务同步,达到集群的目的。

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

如果有三个Eureka,则每一个EurekaServer都需要注册到其它几个Eureka服务中,例如:有三个分别为10086、10087,则:
10086要注册到10087上、10087要注册到10086上

1.6.1 修改Eureka Server配置信息
    server:
      port: ${port:10086}
    
    spring:
      application:
        name: eureka-server
      security:
        user:
          name: admin
          password: 123
    eureka:
      instance:
        hostname: localhost
      client:
        service-url:
          # eureka 服务地址,如果是集群的话;需要指定其它集群eureka地址
    #      defaultZone: http://127.0.0.1:10086/eureka
          defaultZone: ${defaultZone:http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:10086/eureka/}
        # 不注册自己
    #    register-with-eureka: false
        # 不拉取服务
    #    fetch-registry: false
1.6.2 添加VM Options配置信息
  • 在Run/Debug Configuration中配置启动项:

配置EurekaServerApplication启动项的VM Options,传入需要的端口号和defaultZone信息,如下:

-Dport=10086 -DdefaultZone=http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:10087/eureka/

复制一份,命名为EurekaServerApplication10087,配置启动项的VM Options,传入需要的端口号和defaultZone信息,如下:

    -Dport=10087 -DdefaultZone=http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:10086/eureka/
1.6.3 启动测试

分别启动EurekaServerApplication和EurekaServerApplication10087,从http://localhost:10086和http://localhost:10087/可以看到服务列表中各自存在对方的信息,则已实现了Eureka Server的集群配置。

1.6.4 客户端注册服务到EurekaServer

因为EurekaServer不止一个,因此user-service 项目注册服务或者consumer-demo 获取服务的时候,service-url参数需要修改为如下:

    eureka:
      client:
        service-url:
          defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@localhost:10086/eureka,http://${spring.security.user.name}:${spring.security.user.password}@localhost:10087/eureka
1.6.5 服务注册与续约

服务提供者需要向Eureka Server注册服务,并完成服务续约等工作。

  • 服务注册
    服务提供者在启动时会检测配置属性中的eureka.client.register-with-eureka参数,其默认值为true。如果为true,则服务提供者会向Eureka Server发起一个Rest请求,并携带自己的元数据信息,EurekaServer会把这些信息保存到一个双层Map结构中。
    第一层Map的Key就是服务id,一般是配置中的spring.application.name 属性
    第二层Map的key是服务的实例id。一般host+ serviceId + port,例如:localhost:user-service;值则是服务的实例对象,也就是说一个服务,可以同时启动多个不同实例,形成集群。

默认注册时使用的是主机名或者localhost,如果想使用IP进行注册,则可以在服务提供方添加如下配置:

    eureka:
      instance:
        ip-address: 127.0.0.1
        # 倾向于使用ip
        prefer-ip-address: true
  • 服务续约
    在服务注册完成后,服务提供者会维持一个心跳检测(即定时向EurekaServer发起Rest请求),告诉EurekaServer当前服务是否可用,我们称为服务的续约(renew)。
    有两个重要参数可以修改服务续约行为,如下:
    eureka:
      instance:
      	# 服务失效的时间间隔,默认为90秒
        lease-expiration-duration-in-seconds: 90
        # 服务续约的时间间隔,默认为30秒
        lease-renewal-interval-in-seconds: 30

即默认请情况下每隔30秒服务会向注册中心发送一次心跳,证明服务还可用。如果超过90秒没有发送心跳,EurekaServer会认为当前服务不可用,会定时从服务列表中移除这个服务,定时时间由参数eureka.server.eviction-interval-timer-in-ms指定,默认即可,无需设定。

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

如下的配置都是在Eureka Server服务端进行:

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

  • 失效剔除
    有时我们的服务可能由于内存溢出或网络故障等原因使得服务不能正常的工作,而服务注册中心并未收到“服务下线”的请求。相对于服务提供者的“服务续约”操作,服务注册中心在启动时会创建一个定时任务,默认每隔一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除。
    可以通过eureka.server.eviction-interval-timer-in-ms 参数对其进行修改,单位是毫秒。

  • 自我保护
    我们关停一个服务,很可能会在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的自我保护机制。当服务未按时进行心跳续约时,Eureka会统计服务实例最近15分钟心跳续约的比例是否低于了85%。

在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。

Eureka在这段时间内不会剔除任何服务实例,直到网络恢复正常。生产环境下这很有效,保证了大多数服务依然可用,不过也有可能获取到失败的服务实例,因此服务调用者必须做好服务的失败容错。

可以通过下面的配置来关停自我保护:

    eureka:
      server:
        enable-self-preservation: false # 关闭自我保护模式(缺省为打开)

Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。它默认为我们提供了很多负载均衡算法,如轮询、随机等。当然,也可以自定义负载均衡算法。

1.7 负载均衡Ribbon

启动两个user-service实例,端口分别为9091和9092。

2.2 开启负载均衡
在RestTemplate方法上添加@LoadBalanced注解:
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }

修改控制层服务调用方式:

        @GetMapping("/{id}")
        public User queryById(@PathVariable Long id){
            String url = "http://user-service/user/" + id;
            return restTemplate.getForObject(url, User.class);
        }

Ribbon默认的负载均衡策略是轮询。SpringBoot也提供了修改负载均衡规则的配置入口,如下:

    user-service:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

格式:{服务名称}.ribbon.NFLoadBalancerRuleClassName

文章来源:https://blog.csdn.net/SUNBOYmxbsH/article/details/135033472
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。