Spring Cloud核心组件——Eureka服务注册中心

1.1 关于服务注册中⼼

注意:服务注册中⼼本质上是为了解耦服务提供者和服务消费者。

对于任何⼀个微服务,原则上都应存在或者⽀持多个提供者(⽐如简历微服务部署多个实例),这是由微服务的分布式属性决定的。更进⼀步,为了⽀持弹性扩缩容特性,⼀个微服务的提供者的数量和分布往往是动态变化的,也是⽆法预先确定的。因此,原本在单体应⽤阶段常⽤的静态LB机制就不再适⽤了,需要引⼊额外的组件来管理微服务提供者的注册与发现,⽽这个组件就是服务注册中⼼。

1.1.1 服务注册中⼼⼀般原理

分布式微服务架构中,服务注册中⼼⽤于存储服务提供者地址信息、服务发布相关的属性信息,消费者通过主动查询和被动通知的⽅式获取服务提供者的地址信息,⽽不再需要通过硬编码⽅式得到提供者的地址信息。消费者只需要知道当前系统发布了那些服务,⽽不需要知道服务具体存在于什么位置,这就是透明化路由。

1)服务提供者启动

2)服务提供者将相关服务信息主动注册到注册中⼼

3)服务消费者获取服务注册信息:

pull模式:服务消费者可以主动拉取可⽤的服务提供者清单

push模式:服务消费者订阅服务(当服务提供者有变化时,注册中⼼也会主动推送更新后的服务清单给消费者

4)服务消费者直接调⽤服务提供者

另外,注册中⼼也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除;

1.1.3 主流服务中⼼对⽐

  • Zookeeper

Zookeeper它是⼀个分布式服务框架,是Apache Hadoop 的⼀个⼦项⽬,它主要是⽤来解决分布式应 ⽤中经常遇到的⼀些数据管理问题,如:统⼀命名服务、状态同步服务、集群管理、分布式应⽤配置项的管理等。简单来说zookeeper本质=存储+监听通知。

Zookeeper ⽤来做服务注册中⼼,主要是因为它具有节点变更通知功能,只要客户端监听相关服务节点,服务节点的所有变更,都能及时的通知到监听客户端,这样作为调⽤⽅只要使⽤ Zookeeper 的客户端就能实现服务节点的订阅和变更通知功能了,⾮常⽅便。另外,Zookeeper 可⽤性也可以,因为只要半数以上的选举节点存活,整个集群就是可⽤的。

  • Eureka

由Netflflix开源,并被Pivatal集成到SpringCloud体系中,它是基于 RestfulAPI⻛格开发的服务注册与发现组件。

  • Consul

Consul是由HashiCorp基于Go语⾔开发的⽀持多数据中⼼分布式⾼可⽤的服务发布和注册服务软件, 采⽤Raft算法保证服务的⼀致性,且⽀持健康检查。

  • Nacos

Nacos是⼀个更易于构建云原⽣应⽤的动态服务发现、配置管理和服务管理平台。简单来说 Nacos 就是 注册中⼼ + 配置中⼼的组合,帮助我们解决微服务开发必会涉及到的服务注册 与发现,服务配置,服务管理等问题。Nacos 是Spring Cloud Alibaba 核⼼组件之⼀,负责服务注册与发现,还有配置。

组件名

语⾔

 CAP

对外暴露接⼝

Eureka

Java

AP(⾃我保护机制,保证可⽤)

HTTP

Consul

Go

CP

HTTP/DNS

Zookeeper

Java

CP

客户端

Nacos

Java

⽀持AP/CP切换

HTTP

P:分区容错性(⼀定的要满⾜的)

C:数据⼀致性

A:⾼可⽤

CAP不可能同时满⾜三个,要么是AP,要么是CP

1.2 服务注册中⼼组件 Eureka

  • Eureka 基础架构

  •  Eureka 交互流程及原理

Eureka 包含两个组件:Eureka Server 和 Eureka Client,Eureka Client是⼀个Java客户端,⽤于简化与Eureka Server的交互;Eureka Server提供服务发现的能⼒,各个微服务启动时,会通过Eureka Client向Eureka Server 进⾏注册⾃⼰的信息(例如⽹络信息),Eureka Server会存储该服务的信息;

1)图中us-east-1c、us-east-1d,us-east-1e代表不同的区也就是不同的机房2)图中每⼀个Eureka Server都是⼀个集群。

3)图中Application Service作为服务提供者向Eureka Server中注册服务,Eureka Server接受到注册事件会在集群和分区中进⾏数据同步,Application Client作为消费端(服务消费者)可以从Eureka Server中获取到服务注册信息,进⾏服务调⽤。

4)微服务启动后,会周期性地向Eureka Server发送⼼跳(默认周期为30秒)以续约⾃⼰的信息

5)Eureka Server在⼀定时间内没有接收到某个微服务节点的⼼跳,Eureka Server将会注销该微服务节点(默认90秒)

6)每个Eureka Server同时也是Eureka Client,多个Eureka Server之间通过复制的⽅式完成服务注册列表的同步

7)Eureka Client会缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使⽤缓存中的信息找到服务提供者

Eureka通过⼼跳检测、健康检查和客户端缓存等机制,提⾼系统的灵活性、可伸缩性和可⽤性。

 1.3 Eureka应⽤及⾼可⽤集群

1.3.1 搭建单例Eureka Server服务注册中⼼

   1). 在父工程中引入Spring-cloud依赖

      <!--Spring-cloud依赖 -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-dependencies</artifactId>
          <version>Greenwich.RELEASE</version>
          <type>pom</type>
          <scope>import</scope>
      </dependency>

  2). 创建Eureka Server工程引入Eureka Server依赖

    <dependencies>
        <!--eureka server 依赖坐标-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

注意:如果使用jdk9以上需要在⽗⼯程的pom⽂件中⼿动引⼊jaxb的jar,因为Jdk9之后默认没有加载该模块,EurekaServer使⽤到,所以需要⼿动导⼊,否则EurekaServer服务⽆法启动

        <!--引⼊Jaxb,开始-->
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.2.10-b140310.1920</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!--引⼊Jaxb,结束-->

 3). 编写application.yml文件

server:
  port: 8761
spring:
  application:
    name: zql-spring-cloud-eruka
eureka:
  instance:
    hostname: localhost
  client:
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
    register-with-eureka: false  # 不向eureka server 注册自己
    fetch-registry: false  # 不向eureka server 获取服务列表

4). SpringBoot启动类,使⽤@EnableEurekaServer声明当前项⽬为EurekaServer服务

@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEurkaServer8761Application {

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

5). 执⾏启动类SpringCloudEurkaServer8761Application的main函数

访问http://127.0.0.1:8761,如果看到如下⻚⾯(Eureka注册中⼼后台),则表明EurekaServer发布成功

 1.3.2 搭建Eureka Server HA⾼可⽤集群

在互联⽹应⽤中,服务实例很少有单个的。

即使微服务消费者会缓存服务列表,但是如果EurekaServer只有⼀个实例,该实例挂掉,正好微服务消费者本地缓存列表中的服务实例也不可⽤,那么这个时候整个系统都受影响。

在⽣产环境中,我们会配置Eureka Server集群实现⾼可⽤。Eureka Server集群之中的节点通过点对点(P2P)通信的⽅式共享服务注册表。我们开启两台 Eureka Server 以搭建集群。

 (1)修改本机host属性

由于是在个⼈计算机中进⾏测试很难模拟多主机的情况,Eureka配置server集群时需要执⾏host地址。 所以需要修改个⼈电脑中host地址

127.0.0.1       zhangqingleEurekaServerA

127.0.0.1       zhangqingleEurekaServerB

(2) 将刚刚搭建的Eureka Server 赋值一份,并修改其配置

修改原8761 application.yml文件

server:
  port: 8761
spring:
  application:
    name: zql-spring-cloud-eruka
eureka:
  instance:
    hostname: zhangqingleEurekaServerA
  client:
    service-url:
      defaultZone: http://zhangqingleEurekaServerB:8762/eureka
    register-with-eureka: true  #集群模式设置为true
    fetch-registry: true  #集群模式设置为true

修改复制的 配置文件和端口为8762  

server:
  port: 8762
spring:
  application:
    name: zql-spring-cloud-eruka
eureka:
  instance:
    hostname: zhangqingleEurekaServerB
  client:
    service-url:
      defaultZone: http://zhangqingleEurekaServerA:8761/eureka
    register-with-eureka: true 
    fetch-registry: true  

分别启动8761和8762两个服务

访问http://localhost:8761和http://localhost:8762 可以看出eureka集群已经搭建成功了

 

1.3.3 微服务提供者—>注册到Eureka Server集群

1).创建新的工程,并引入 Eureka Client依赖

        <!--eureka client 依赖坐标-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

2).编写application.yml文件

server:
  port: 8083
spring:
  application:
    name: zqldemoprovider8083
eureka:
  client:
    serviceUrl: # eureka server的路径
      #把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
      defaultZone: http://zhangqingleEurekaServerB:8762/eureka/,http://zhangqingleEurekaServerA:8761/eureka/ 
  instance: #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
    prefer-ip-address: true
  #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

3).编写controller对应demo

@RestController
@RequestMapping("/demo")
public class DemoController {
    
    @RequestMapping("/server/demo")
    public String demo(){
        return "hello Eureka";
    }
}

4).编写启动类

@SpringBootApplication
//@EnableEurekaClient  // 开启Eureka 客户端
@EnableDiscoveryClient // 开启注册中心客户端
public class DemoApplication8083 {

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

注意:

1)从Spring Cloud Edgware版本开始,@EnableDiscoveryClient 或@EnableEurekaClient 可省略。只需加 上相关依赖,并进⾏相应配置,即可将微服务注册到服务发现组件上。

2)@EnableDiscoveryClient和@EnableEurekaClient⼆者的功能是⼀样的。但是如果选⽤的是eureka服务器,那么就推荐@EnableEurekaClient,如果是其他的注册中⼼,那么推荐使⽤@EnableDiscoveryClient,考虑到通⽤性,后期我们可以使⽤@EnableDiscoveryClient

启动类执⾏,在Eureka Server后台界⾯可以看到注册的服务实例

1.3.4 微服务消费者—>注册到Eureka Server集群 

1).创建新的工程,并引入 Eureka Client依赖

        <!--eureka client 依赖坐标-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

2).配置application.yml⽂件

server:
  port: 8082
spring:
  application:
    name: zqldemocomsumer8082
eureka:
  client:
    serviceUrl: # eureka server的路径
      #把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eureka server 可以同步注册表
      defaultZone: http://zhangqingleEurekaServerB:8762/eureka/,http://zhangqingleEurekaServerA:8761/eureka/ 
  instance: #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
    prefer-ip-address: true
    #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

3).在启动类添加注解@EnableDiscoveryClient,开启服务发现

@SpringBootApplication
@EnableDiscoveryClient  //开启服务发现
public class DemoConsumerApplcation8082 {

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

    /**
     * 注⼊RestTemplate
     * @return
     */
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

1.3.5 服务消费者调⽤服务提供者(通过Eureka)

@RestController
@RequestMapping("/autodeliver")
@RefreshScope
public class AutodeliverController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 使用eureka和rest进行访问
     * @return
     */
    @RequestMapping("/check/prod")
    public Integer findResumeOpenState() {
        //1. 获取Eureka中注册的实例
        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("ZQLDEMOPROVIDER8083");
        //2. 获取实例
        ServiceInstance serviceInstance = serviceInstanceList.get(0);

        //3. 根据实例信息拼接地址
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String url = "http://" + host + ":" + port + "/demo/server/demo";
        //4. 消费者直接调用提供者
        Integer forObject = restTemplate.getForObject(url, Integer.class);

        System.out.println("消费者调用提供者的接口端口为:" + forObject);

        return forObject;
    }
}

启动项目后查看Eureka 注册中心  服务提供者和服务消费者都以注册上来

访问http://localhost:8082/autodeliver/check/prod进行服务调用

 1.4 Eureka细节详解

1.4.1 Eureka元数据详解

Eureka的元数据有两种:标准元数据和⾃定义元数据。

标准元数据:主机名、IP地址、端⼝号等信息,这些信息都会被发布在服务注册表中,⽤于服务之间的调⽤。

⾃定义元数据:可以使⽤eureka.instance.metadata-map配置,符合KEY/VALUE的存储格式。这 些元数据可以在远程客户端中访问。

类似于

instance:

    prefer-ip-address: true

    metadata-map:

         # ⾃定义元数据(kv⾃定义)

          cluster: cl1

          region: rn1

我们可以在程序中可以使⽤DiscoveryClient 获取指定微服务的所有元数据信息

在serviceInstanc中的metadata中保存着原数据的信息

 1.4.2 Eureka客户端详解

服务提供者(也是Eureka客户端)要向EurekaServer注册服务,并完成服务续约等⼯作

服务注册详解(服务提供者)

1)当我们导⼊了eureka-client依赖坐标,配置Eureka服务注册中⼼地址

2)服务在启动时会向注册中⼼发起注册请求,携带服务元数据信息

3)Eureka注册中⼼会把服务的信息保存在Map中。

服务续约详解(服务提供者)

服务每隔30秒会向注册中⼼续约(⼼跳)⼀次(也称为报活),如果没有续约,租约在90秒后到期,然后服务会被失效。每隔30秒的续约操作我们称之为⼼跳检测

往往不需要我们调整这两个配置

#向Eureka服务中⼼集群注册服务

eureka:

    instance:

      # 租约续约间隔时间,默认30秒

       lease-renewal-interval-in-seconds: 30

      # 租约到期,服务时效时间,默认值90秒,服务超过90秒没有发⽣⼼跳,EurekaServer会将服务从列表移除

       lease-expiration-duration-in-seconds: 90

获取服务列表详解(服务消费者)

每隔30秒服务会从注册中⼼中拉取⼀份服务列表,这个时间可以通过配置修改。

往往不需要我们调整

#向Eureka服务中⼼集群注册服务

eureka:

    client:

      # 每隔多久拉取⼀次服务列表

      registry-fetch-interval-seconds: 30

1)服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地

2)每隔30秒,会重新获取并更新数据

3)每隔30秒的时间可以通过配置eureka.client.registry-fetch-interval-seconds修 改

1.4.3 Eureka服务端详解

服务下线

1)当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。 

2)服务中⼼接受到请求后,将该服务置为下线状态

失效剔除

Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进⾏检查,如果发现实例在在⼀定时间(此值由客户端设置的eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到⼼跳,则会注销此实例。

⾃我保护

服务提供者 —> 注册中⼼

定期的续约(服务提供者和注册中⼼通信),假如服务提供者和注册中⼼之间的⽹络有点问题,不代表服务提供者不可⽤,不代表服务消费者⽆法访问服务提供者如果在15分钟内超过85%的客户端节点都没有正常的⼼跳,那么Eureka就认为客户端与注册中⼼出现了⽹络故障,Eureka Server⾃动进⼊⾃我保护机制。

为什么会有⾃我保护机制?

默认情况下,如果Eureka Server在⼀定时间内(默认90秒)没有接收到某个微服务实例的⼼跳,Eureka Server将会移除该实例。但是当⽹络分区故障发⽣时,微服务与Eureka Server之间⽆法正常通信,⽽微服务本身是正常运⾏的,此时不应该移除这个微服务,所以引⼊了⾃我保护机制。

服务中⼼⻚⾯会显示如下提示信息

当处于⾃我保护模式时

1)不会剔除任何服务实例(可能是服务提供者和EurekaServer之间⽹络问题),保证了⼤多数服务依然可⽤

2)Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可⽤,当⽹络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

3)在Eureka Server⼯程中通过eureka.server.enable-self-preservation配置可⽤

关停⾃我保护,默认值是打开

 eureka:

    server:

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

经验:建议⽣产环境打开⾃我保护机制

猜你喜欢

转载自blog.csdn.net/xiaozhang_man/article/details/124084681