Eureka注册中心

Eureka注册中心

一、什么是注册中心

注册中心可以说是微服务架构中的“通讯录”,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。

总结:服务注册中心的作用就是服务的注册服务的发现

二、常见的注册中心

  • Netflix Eureka
  • Alibaba Nacos
  • HashiCorp Consul
  • Apache ZooKeeper
特性EurekaNacosConsulZookeeper
CAPAPCP+APCPCP
健康检查Client BeatTCP/HTTP/MYSQL/Client BeatTCP/HTTP/gRPC/CmdKeep Alive
雪崩保护
自动注销实例支持支持不支持不支持
访问协议HTTPHTTP/DNSHTTP/DNSTCP
监听支持支持支持支持支持
多数据中心支持支持支持不支持
跨注册中心同步不支持支持支持不支持
SpringCloud集成支持支持支持支持

三、为什么需要注册中心

在分布式系统中,我们不仅仅是需要在注册中心找到服务和服务地址的映射关系这么简单,我们还需要考虑更多复杂的问题:

  • 服务注册后,如何及时发现
  • 服务宕机后,如何及时下线
  • 服务如何有效的水平扩展
  • 服务发现时,如何进行路由
  • 服务异常时,如何进行降级
  • 注册中心如何实现自身的高可用

四、注册中心解决了什么问题

  • 服务管理
  • 服务的依赖管理

五、什么是Eureka注册中心

Eureka是Netflix开发的服务发现组件,本身是一个基于REST的服务。Spring Cloud将它集成在子项目Spring Cloud Netflix中,实现Spring Cloud的注册与发现,同时还提供了负载均衡、故障转移等能力。

六、Eureka注册中心三种角色

image-20220410155734724

  • Eureka Server

    通过Register、Get、Renew等接口提供服务的注册和发现

  • Application Service(Service Provider)

    服务提供方,把自身的服务实例注册到 Eureka Server 中

  • Application Client(Service Consumer)

    服务调用方,通过 Eureka Server 获取服务列表,消费服务

  • Register(服务注册):把自己的IP和端口注册给Eureka

  • Renew(服务续约):发送心跳包,每30秒发送一次,告诉Eureka自己还活着,如果90秒还未发送心跳,宕机

  • Cancel(服务下线):当provider关闭时会向Eureka发送消息,把自己从服务列表中删除

  • Get Register(获取注册列表):获取其他服务列表

  • Replicate(数据同步):Eureka集群中的数据复制与同步

  • make remote call(远程调用):服务间的远程调用

七、HelloWorld

实例代码:https://gitee.com/junweihu/eureka-demo

  1. 创建父项目,添加依赖

    <?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>org.example</groupId>
        <artifactId>eureka-demo</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
        <modules>
            <module>eureka-server</module>
        </modules>
    
        <!-- 集成spring-boot-starter-parent依赖 -->
        <!-- 使用集成方式了,实现复用 -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.4.RELEASE</version>
        </parent>
    
        <!-- 集中定义依赖组件版本号,但不引入,在子工程中用到声明的依赖时,可以不加依赖的版本号,统一管理工程中用到的依赖版本号 -->
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
        </properties>
    
        <!-- 项目依赖管理,父项目只是声明依赖,子项目需要写明需要的依赖(可以省略版本信息) -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    </project>
    
  2. 创建子项目,添加依赖

    <?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>eureka-demo</artifactId>
            <groupId>org.example</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>eureka-server</artifactId>
    
      <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-starter-web</artifactId>
        </dependency>
    
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
          <exclusions>
            <exclusion>
              <groupId>org.junit.vintage</groupId>
              <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
    
      </dependencies>
    </project>
    
  3. 配置文件

    server:
      port: 8761
    
    spring:
      application:
        name: eureka-server
    
    eureka:
      instance:
        hostname: localhost           # 主机名,不配置的时候将根据操作系统的主机名获取
      client:
        register-with-eureka: false   # 是否将自己注册到注册中心
        fetch-registry: false         # 是否从注册中心获取注册信息
        service-url:                  # 注册中心对外暴露的注册地址
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    
  4. 启动类

    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaServerApplication
    {
        public static void main( String[] args )
        {
            SpringApplication.run(EurekaServerApplication.class);
        }
    }
    
  5. 访问

    访问:http://localhost:8761/

    image-20220410174104911

八、高可用Eureka注册中心

注册中心

  1. 创建项目

    在刚才的父工程下再创建一个 eureka-server02 注册中心的项目,如果是多台机器部署不用修改端口,通过IP区分服务,如果在一台机器上部署需要修改端口区分服务。

  2. 修改配置文件

    eureka-server

    server:
      port: 8761
    
    spring:
      application:
        name: eureka-server
    
    eureka:
      instance:
        hostname: localhost           # 主机名,不配置的时候将根据操作系统的主机名获取
      client:
    #    register-with-eureka: false   # 是否将自己注册到注册中心
    #    fetch-registry: false         # 是否从注册中心获取注册信息
        # 注册中心多个节点相互注册
        service-url:                  # 注册中心对外暴露的注册地址
          defaultZone: http://localhost:8762/eureka/
    

    eureka-server02

    server:
      port: 8762
    
    spring:
      application:
        name: eureka-server
    
    eureka:
      instance:
        hostname: localhost           # 主机名,不配置的时候将根据操作系统的主机名获取
      client:
    #    register-with-eureka: false   # 是否将自己注册到注册中心
    #    fetch-registry: false         # 是否从注册中心获取注册信息
        # 注册中心多个节点相互注册
        service-url:                  # 注册中心对外暴露的注册地址
          defaultZone: http://localhost:8761/eureka/
    
  3. 访问

    访问http://localhost:8761/或者http://localhost:8762/显示如下说明互相注册成功。

    image-20220411202339989

  4. 显示IP+端口

    修改配置文件

    eureka:
      instance:
        hostname: localhost           # 主机名,不配置的时候将根据操作系统的主机名获取
        prefer-ip-address: true       # 是否使用IP地址注册
        instance-id: ${spring.cloud.client.ip-address}:${server.port} #ip:port
    

    修改后结果

    image-20220411203630659

服务提供者&服务消费者

  1. 创建项目

    创建子模块service-provider、service-consumer

  2. 配置文件

    service-provider

    server:
      port: 7070
    
    spring:
      application:
        name: service-provider
    
    eureka:
      instance:
        hostname: localhost           # 主机名,不配置的时候将根据操作系统的主机名获取
        prefer-ip-address: true       # 是否使用IP地址注册
        instance-id: ${spring.cloud.client.ip-address}:${server.port} #ip:port
      client:
    #    register-with-eureka: false   # 是否将自己注册到注册中心
    #    fetch-registry: false         # 是否从注册中心获取注册信息
        # 注册中心多个节点相互注册
        service-url:                  # 注册中心对外暴露的注册地址
          defaultZone: http://localhost:8761/eureka/,http://localhost:8761/eureka/
    

    service-consumer

    server:
      port: 9090
    
    spring:
      application:
        name: service-consumer
    
    eureka:
     instance:
        hostname: localhost           # 主机名,不配置的时候将根据操作系统的主机名获取
        prefer-ip-address: true       # 是否使用IP地址注册
        instance-id: ${spring.cloud.client.ip-address}:${server.port} #ip:port
      client:
        registry-fetch-interval-seconds: 10 #间隔多久拉取注册信息
        service-url:                  # 注册中心对外暴露的注册地址
          defaultZone: http://localhost:8761/eureka/,http://localhost:8761/eureka/
    
  3. 启动类

    @EnableEurekaClient
    @SpringBootApplication
    public class ServiceProviderApplication
    {
        public static void main( String[] args )
        {
            SpringApplication.run(ServiceProviderApplication.class);
        }
    }
    
  4. 访问注册中心

    image-20220412171158182

九、CAP原则

CAP原则,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

特性定理
Consistency一致性,也叫数据原子性,系统在执行某项操作后仍然处于一致的状态。在分布式系统中,更新操作执行成功后所有的用户都应该读到最新的数据,这样的系统被认为是具有强一致性的。等同于所有节点访问同一份最新的数据副本。
Availability可用性,每一个操作总是能在一定的时间内返回结果,这里需要注意的是“一定时间内”和“返回结果”。一定时间内指的是在可以容忍的范围内返回结果,结果可以是成功或者失败,且不保证获取的数据是最新数据。
Partition tolerance分区容错性,分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。

取舍策略

  • CA:如果不要求P(不允许分区即单体应用),则强一致性和可用性是可以保证的。但是放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没有办法部署子节点,这是违背分布式系统设计初衷的。
  • CP:如果不要求A(可用性),相当于每个请求都需要在服务器之间保持一致,而P(分区)会导致同步时间无限长(也就是等待数据同步完成才能正常访问服务),一旦发送网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统。
  • AP:要高可用并允许分区,则需要放弃一致性。一旦分区发生,节点之间可能失去联系,为了高可用,每个节点只能使用本地数据提供服务,而这样会导致全局数据的不一致性。

总结

现如今,对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,节点只会越来越多,所以节点故障、网络故障是常态,因此分区容错也就成为了一个分布式系统必须要面对的问题。那么就只能在C和A之间进行取舍。但对于传统的项目就可能有所不同,比如银行的转账系统,涉及到金钱的对于数据一致性不能做出让步,C必须保证,出现网络故障的话,宁可停止服务。

总而言之,没有最好的策略,好的系统应该是根据业务场景来进行架构设计的,只有合适的才是最好的。

十、Eureka自我保护

启动自我保护条件

一般情况下,服务在Eureka上注册后,会每30秒发送心跳包,Eureka通过心跳来判断服务是否健康,同时会定期删除超过90秒没有发送心跳的服务。

有两种情况会导致Eureka Server收不到服务的心跳

  • 服务自身的原因
  • 微服务与Eureka之间的网络故障

自我保护模式

Eureka Server在运行期间会统计心跳失败比例在15分钟之内是否低于85%,如果低于85%,Eureka Server会将这些实例保护起来,让这些实例不会过期,同时提示一个警告。这种算法叫做Eureka Server的自我保护模式。

为什么要启动自我保护

  • 因为同时保留“好数据”和“坏数据”总比丢掉任何数据要好,当网络故障恢复后,这个Eureka节点会退出“自我保护模式”
  • Eureka还有客户端缓存功能,即便是Eureka集群中所有节点都宕机失效,微服务的provider和consumer都能正常通信
  • 微服务的负载均衡策略会自动剔除死亡的服务节点

如何关闭自我保护

注册中心配置自我保护

eureka:
  server:
    enable-self-preservation: false       #是否开启自我保护

image-20220414105329034

十一、Eureka优雅停服

这里使用actuator实现。

  1. 添加依赖

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-actuator</artifactId>
          </dependency>
    
  2. 配置文件

    服务提供者配置度量指标监控与健康检查

    management:
      endpoints:
        web:
          exposure:
            include: shutdown        #开启shutdown端点访问
      endpoint:
        shutdown:
          enabled: true              #开启shutdown实现优雅停服     
    
  3. 优雅停服

    使用POST请求访问:http://localhost:7070/actuator/shutdown 效果如下

    image-20220414111330226

    shutdown之前

    image-20220414111022856

    shutdown之后

    image-20220414111305183

十二、Eureka安全认证

通过用户名和密码访问Eureka Server。

  1. 添加依赖

    注册中心添加security依赖

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-security</artifactId>
          </dependency>
    
  2. 配置文件

    注册中心配置安全认证

    spring:
      application:
        name: eureka-server
      security:                   #安全认证
        user:
          name: root
          password: 123456
    
  3. 修改访问集群节点的url

    eureka:
      server:
        enable-self-preservation: false       #是否开启自我保护
        eviction-interval-timer-in-ms: 60000  #清理间隔
      instance:
        hostname: localhost           # 主机名,不配置的时候将根据操作系统的主机名获取
        prefer-ip-address: true       # 是否使用IP地址注册
        instance-id: ${spring.cloud.client.ip-address}:${server.port} #ip:port
      client:
        # 注册中心多个节点相互注册
        service-url:                  # 注册中心对外暴露的注册地址
          defaultZone: http://root:123456@localhost:8762/eureka/    #配置访问注册中心用户名和密码
    
  4. 效果

    image-20220414114000510

  5. 过滤CSRF

    Eureka会自动化配置CSRF防御机制,Spring Security认为POST,PUT,DELETE http methods都是有风险的,如果这些method发送过程中没有带上CSRF token的话,会被拦截返回403 forbidden。

    使CSRF忽略/eureka/**的所有请求

    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
            // 忽略/eureka/**的所有请求
            http.csrf().ignoringAntMatchers("/eureka/**");
        }
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值