从零开始学习微服务 - Eureka服务注册中心、服务注册、Eureka细节详解、自我保护机制

1. Eureka服务

1.1 Eureka服务注册中心

  • 常用的服务注册中心:Eureka、Nacos、Zookeeper、Consul
1.1.1 关于注册中心
  • 注意:服务注册中心本质上是为了解耦服务提供者和服务消费者。
  • 服务消费者 --> 服务提供者
  • 服务消费者 --> 服务注册中心 --> 服务提供者
  • 对于任何一个微服务,原则上都应存在或者支持多个提供者(比如商品微服务部署多个实例),这
    是由微服务的分布式属性决定的
  • 更进一步,为了支持弹性扩、缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也
    是无法预先确定的。因此,原本在单体应用阶段常用的静态LB机制就不再适用了,需要引入额外的组件
    来管理微服务提供者的注册与发现,而这个组件就是服务注册中心。
1.1.2 注册中心实现原理

image-20220211182447726

image-20220211182455670

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

  • (1) 服务提供者启动 :

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

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

    • pull模式:服务消费者可以主动拉取可用的服务提供者清单
    • push模式:服务消费者订阅服务(当服务提供者有变化时,注册中心也会主动推送更新后的服务清单给消费者
  • (4) 服务消费者直接调用服务提供者另外,注册中心也需要完成服务提供者的健康监控,当发现服务提供者失效时需要及时剔除;

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

1.2 主流服务中心

1.2.1 Zookeeper
  • Dubbo + Zookeeper
  • Zookeeper它是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决
    分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布
    式应用配置项的管理等。
  • 简单来说zookeeper本质 = 存储 + 监听通知。
  • Zookeeper 用来做服务注册中心,主要是因为它具有节点变更通知功能,只要客户端监听相
    关服务节点,服务节点的所有变更,都能及时的通知到监听客户端,这样作为调用方只要使用
    Zookeeper 的客户端就能实现服务节点的订阅和变更通知功能了,非常方便。
  • 另外,Zookeeper可用性也可以,因为只要半数以上的选举节点存活,整个集群就是可用的,最少节点数为3。
1.2.2 Eureka
  • 由Netflix开源,并被Pivatal集成到SpringCloud体系中,它是基于 RestfulAPI 风格开发的服务
    注册与发现组件。
1.2.3 Consul
  • Consul是由HashiCorp基于Go语言开发的支持多数据中心分布式高可用的服务发布和注册服
    务软件, 采用Raft算法保证服务的一致性,且支持健康检查
1.2.4 Nacos
  • Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。简单来说
    Nacos 就是 注册中心 + 配置中心的组合,帮助我们解决微服务开发必会涉及到的服务注册 与发
    现,服务配置,服务管理等问题。

  • Nacos 是 Spring Cloud Alibaba 核心组件之一,负责服务注册与发现,还有配置

    image-20220211183534142

  • CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用
    性)、Partition tolerance(分区容错性),最多只能同时三个特性中的两个,三者不可兼得。

    • P:分区容错性:分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用
      性的服务(一定的要满足的)
    • C:数据一致性:all nodes see the same data at the same time
    • A:高可用:Reads and writes always succeed
    • CAP不可能同时满足三个,要么是AP,要么是CP
1.2.5 服务注册中心组件
  • 服务注册中心的一般原理、对比了主流的服务注册中心方案,目光聚焦Eureka。

  • Eureka 基础架构

    image-20220211183709933

    image-20220211183714066

  • Eureka 交互流程及原理

    image-20220211183723074

    image-20220211183737014

  • 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作为消费端(服务消费者)可以从EurekaServer中获取到服务注册信息,进行服务调用。

  • (4) 微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒,默认Eureka Server
    90S会将还没有续约的给剔除)以续约自己的信息

  • (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. 服务提供者(商品微服务注册到集群)
  2. 服务消费者(页面静态化微服务注册到Eureka/从Eureka Server获取服务信息)
  3. 完成调用
1.3.2 搭建Eureka Server
  • 创建一个项目名为 lg-cloud-eureka 的 maven 项目
1.3.3 添加配置
  • 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">
        <parent>
            <artifactId>lg-parent</artifactId>
            <groupId>cn.knightzz</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>lg-cloud-eureka</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <!--Eureka server依赖-->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <dependencies> 
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
        </dependencies>
    
    </project>
    
  • application.yml

    # Eureka server服务端口
    server:
      port: 9200
    spring:
      application:
        # 应用名称,会在Eureka中作为服务的id标识
        name: lg-cloud-eureka-server
    eureka:
      instance:
        hostname: 127.0.0.1
      client:
        # 客户端与EurekaServer交互的地址,如果是集群,也需要写其它Server的地址
        service-url:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
        # 自己就是服务不需要注册自己
        register-with-eureka: false
        # 自己就是服务不需要从Eureka Server获取服务信息,默认为true,置 为false
        fetch-registry: false
    
  • hostname 需要设置为 127.0.0.1, 若是 localhost 可能会报错

  • 参考 : unable-to-send-heartbeat

1.3.4 关于jaxb
  • 注意:在父工程的pom文件中手动引入jaxb的jar,因为Jdk9之后默认没有加载该模块,Eureka
    Server使用到,所以需要手动导入,否则EurekaServer服务无法启动

  • 所以需要在 lg-parent 添加 jaxb 配置

      <!--引入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>
    
1.3.5 关于版本问题
  • 如果版本不兼容可能会出现无法启动的问题, 报一些过滤器无法创建之类的错误,

  • 所以一定要确定 SpringBoot和Eureka的版本

     No qualifying bean of type 'javax.servlet.Filter' available: expected at least
    
1.3.6 创建启动类
  • EurekaApplication, @EnableEurekaServer 开启 Eureka

    package cn.knightzz;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    /**
     * @author 王天赐
     * @title: EurekaApplication
     * @projectName springcloud-lg
     * @description:
     * @website http://knightzz.cn/
     * @github https://github.com/knightzz1998
     * @date 2022/2/11 20:35
     */
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(EurekaApplication.class,args);
        }
    }
    
    
  • 访问http://127.0.0.1:9200,如果看到如下页面(Eureka注册中心后台),则表明EurekaServer发布成功

    image-20220211210119104

    image-20220211210125876

    image-20220211205932629

1.4 Eureka微服务注册

  • 商品微服务和页面静态化微服务注册到Eureka
1.4.1 添加配置
  • 分别在 lg-service-page 和 lg-service 的 pom文件中添加Eureka Client依赖

  • 这里需要注意的是, 可能会出现找不到依赖的问题, 这是因为没有指定版本

  • 具体的版本需要点击 Greenwich.RELEASE 即可查看对应的spring-cloud 版本

    <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
    
  • 添加 Eureka Client客户端

            <!--Eureka client-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
                <version>2.1.2.RELEASE</version>
            </dependency>
    
  • 添加 eureka 配置

    eureka:
      client:
        # eureka server的路径
        service-url:
          defaultZone: http://127.0.0.1:9200/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@
    
  • 需要注意的是, 请求路径不能是 localhost , 需要是 127.0.0.1 , 否则会出现

    was-unable-to-send-heartbeat
    
  • 参考 :

1.4.2 添加注解
  • 在启动类上添加服务发现注解 @EnableDiscoveryClient

    @SpringBootApplication
    @EnableDiscoveryClient
    public class Application {}
    
  • 启动后 http://localhost:9200/

    image-20220211221603041

1.5 Eureka细节详解

1.5.1 Eureka元数据详解
  • Eureka的元数据有两种:标准元数据和自定义元数据。

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

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

  • 在 lg-cloud-eureka 中添加

    eureka:
      instance:
        hostname: 127.0.0.1
        metadata-map:
          ip: 192.168.200.128
          port: 10000
          user: admin
          pwd: 123456
    
1.5.2 客户端读取元数据
  • 我们可以在程序中可以使用DiscoveryClient 获取指定微服务的所有元数据信息

  • 在 lg-service-page 里添加Controller获取元数据信息

    package cn.knightzz.page.controller;
    
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * @author 王天赐
     * @title: MetadataController
     * @projectName springcloud-lg
     * @description:
     * @website http://knightzz.cn/
     * @github https://github.com/knightzz1998
     * @date 2022/2/12 10:51
     */
    @RestController
    @RequestMapping("/metadata")
    public class MetadataController {
    
        @Resource
        private DiscoveryClient discoveryClient;
    
        @RequestMapping("show")
        public String showMetaData() {
    
            StringBuilder result = new StringBuilder();
            // 获取实例, 因为是集群方式, 所以一个微服务可能会有多个服务
            List<ServiceInstance> instances = discoveryClient.getInstances("lg-service-page");
            for (ServiceInstance instance : instances) {
                // 获取服务元数据
                Map<String, String> metadata = instance.getMetadata();
                Set<Map.Entry<String, String>> entries = metadata.entrySet();
                for (Map.Entry<String, String> entry : entries) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    result.append("key:").append(key).append(",value:").append(value);
                }
            }
            return result.toString();
        }
    }
    
    
  • debug查看元数据

    image-20220212123839070

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

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

    • 当我们导入了eureka-client依赖坐标,配置Eureka服务注册中心地址
    • 服务在启动时会向注册中心发起注册请求,携带服务元数据信息
    • Eureka注册中心会把服务的信息保存在Map中。
  • 服务续约详解(服务提供者)

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

  • Eureka Client :30S续约一次,在Eureka Server更新自己的状态 (Client端进行配置)

  • Eureka Server:90S还没有进行续约,将该微服务实例从服务注册表(Map)剔除 (Client端进行
    配置)

  • Eureka Client: 30S拉取服务最新的注册表并缓存到本地 (Client端进行配置)

  • 往往不需要我们调整这两个配置, 默认就可以, 如果需要(在 lg-cloud-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
    
  • 服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地

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

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

1.5.4 Eureka服务端详解
  • 服务下线:
    • 当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。
    • 服务中心接受到请求后,将该服务置为下线状态
  • 失效剔除
    • Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进行检查,如果发现实例在在一定时间(此值由客户端设置的eureka.instance.lease-expiration-duration-inseconds定义,默认值为90s)内没有收到心跳,则会注销此实例。
1.5.5 自我保护机制
  • 自我保护机制:

    • 自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的
      健壮、稳定的运行。
    • 自我保护机制的工作机制是:如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么
      Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
    • Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
    • Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前
      节点依然可用。
    • 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
      因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半
      不可用的情况会导致整个集群不可用而变成瘫痪
  • 为什么会有自我保护机制

    • 默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,
      Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。

    • 服务中心页面会显示如下提示信息

      image-20220212172608235

  • 我们在单机测试的时候很容易满足心跳失败比例在 15 分钟之内低于 85%,这个时候就会触发 Eureka
    的保护机制,一旦开启了保护机制(默认开启),则服务注册中心维护的服务实例就不是那么准确了,
    此时我们通过修改Eureka Server的配置文件来关闭保护机制,这样可以确保注册中心中不可用的实例被
    及时的剔除

  • 经验:建议生产环境打开自我保护机制

1.6 关于无法获取心跳机制的问题

  • was unable to send heartbeat!
    • 有一定原因是需要等待几分钟, 在服务中心注册完成后, 才可以, 如果刚启动立马请求获取元数据
    • 存在失败的可能
  • Cannot execute request on any known server
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

兀坐晴窗独饮茶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值