SpringCloud系列之一---搭建高可用的Eureka注册中心

前言

本篇文章主要介绍的是SpringCloud相关知识、微服务架构以及搭建服务注册与发现的服务模块(Eureka)以及Eureka集群。

GitHub源码链接位于文章底部。

什么是SpringCloud

Spring Cloud 是一系列框架的有序集合。 它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发, 如服务发现注册、配置中心、消息总线、负载均衡、熔断器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。Spring 并没有复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 SpringBoot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

SpringCloud 与 SpringBoot 的关系

Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;Spring Boot 使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。
Spring Boot可以离开Spring Cloud独立使用开发项目, 但是Spring Cloud离不开Spring Boot,属于依赖的关系。

SpringCloud 主要组件

| 用途 | 组件|
| ------------ |
| 服务发现|Netflix Eureka |
| 服务调用|Netflix Feign |
| 熔断器|Netflix Hystrix |
| 服务网关|Netflix Zuul |
| 分布式配置|Spring Cloud Config |
|消息总线| Spring Cloud Bus|

SpringCloud 与 Dubbo 对比

dubbo由于是二进制的传输,占用带宽会更少。
springCloud是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大。
dubbo的开发难度较大,原因是dubbo的jar包依赖问题很多大型工程无法解决。
Dubbo只是实现了服务治理,而Spring Cloud下面有很多个子项目分别覆盖了微服务架构下的方方面面,服务治理只是其中的一个方面,一定程度来说,Dubbo 只是 Spring CloudNetflix 中的一个子集。

Dubbo SpringCloud
服务注册中心 Zookeeper Spring Cloud Netflix Eureka
服务调用方式 RPC REST API
服务网关 Spring Cloud Netflix Zuul /Spring Cloud GateWay
熔断器 不完善 Spring Cloud Netflix Hystrix
分布式配置 Spring Cloud Config
服务跟踪 Spring Cloud Sleuth
消息总线 Spring Cloud Bus
数据流 Spring Cloud Stream
批量任务 Spring Cloud Task
...... ...... ......

SpringCloud 的版本

SpringCloud 由于是一系列框架组合,为了避免与包含的自框架版本产生混淆,采用伦敦地铁站的名称作为版本名,形式为版本名+里程碑号。M9为第 9 个里程碑版本。以下是SpringBoot与Spring Cloud版本的对照表。

Spring Boot Spring Cloud
1.2.x Angel 版本
1.3.x Brixton 版本
1.4.x Camden 版本
1.5.x Dalston 版本、 Edgware 版本
2.0.x Finchley 版本

服务发现组件 Eureka

Eureka是Netflix 开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。Eureka包含两个组件:Eureka Server和Eureka Client。

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

自我保护机制

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就别一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。但是在短时间内丢失大量的实例心跳,这时候EurekaServer会开启自我保护机制,Eureka不会踢出该服务,这就是Eureka的自我保护机制。
产生原因:在开发测试时,需要频繁地重启微服务实例,但是我们很少会把eureka server一起重启(因为在开发过程中不会修改eureka注册中心),当一分钟内收到的心跳数大量减少时,会触发该保护机制。可以在eureka管理界面看到Renews threshold和Renews(last min),当后者(最后一分钟收到的心跳数)小于前者(心跳阈值)的时候,触发保护机制,会出现红色的警告:

EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.

从警告中可以看到,eureka认为虽然收不到实例的心跳,但它认为实例还是健康的,eureka会保护这些实例,不会把它们从注册表中删掉。

该保护机制的目的是避免网络连接故障,在发生网络故障时,微服务和注册中心之间无法正常通信,但服务本身是健康的,不应该注销该服务,如果eureka因网络故障而把微服务误删了,那即使网络恢复了,该微服务也不会重新注册到eureka server了,因为只有在微服务启动的时候才会发起注册请求,后面只会发送心跳和服务列表请求,这样的话,该实例虽然是运行着,但永远不会被其它服务所感知。所以,eureka server在短时间内丢失过多的客户端心跳时,会进入自我保护模式,该模式下,eureka会保护注册表中的信息,不在注销任何微服务,当网络故障恢复后,eureka会自动退出保护模式。自我保护模式可以让集群更加健壮。

但是我们在开发测试阶段,需要频繁地重启发布,如果触发了保护机制,则旧的服务实例没有被删除,这时请求有可能跑到旧的实例中,而该实例已经关闭了,这就导致请求错误,影响开发测试。所以,在开发测试阶段,我们可以把自我保护模式关闭,只需在eureka server配置文件中加上如下配置即可:

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客户端与 Eureka 服务端进行交互的地址
      defaultZone: http://127.0.0.1:${server.port}/eureka
    #是否将自己注册到Eureka服务中,本身就是注册中心所以无需注册
    register-with-eureka: false
    #是否从Eureka中检索注册信息,本身就是注册中心所以无需检索
    fetch-registry: false
  server:
    # 测试时关闭自我保护机制,保证不可用服务及时踢出
    enable-self-preservation: false
    ##剔除失效服务间隔
    eviction-interval-timer-in-ms: 2000

在eureka client配置文件中加上:

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客户端与 Eureka 服务端进行交互的地址
      defaultZone: http://127.0.0.1:8100/eureka/
  # 心跳检测检测与续约时间
  # 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
  instance:
    # Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则)
    lease-renewal-interval-in-seconds: 1
    # Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己)
    lease-expiration-duration-in-seconds: 2

但在生产环境,不会频繁重启,所以,一定要把自我保护机制打开,否则网络一旦中断,就无法恢复。
当然关于自我保护还有很多个性化配置,这里不详细说明。

注意考虑网络不可达情况下:调用接口幂等、重试、补偿等。

创建工程

1. 目前工程结构

2. 首先创建父工程springcloud,以后这个工程下存放子工程eureka,feign,zuul等组件

将该工程的src文件夹删除,在pom文件中添加依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath ></relativePath>
    </parent>

    <dependencies>
        <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>
        </dependency>
    </dependencies>
	
    <!--引用仓库-->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <!--管理依赖,子项目中的依赖不用列出版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.M9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

创建eureka父工程

新建springcloud-eureka项目,在pom文件中添加依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

创建eureka服务端

以springcloud-eureka为父工程,新建springboot-eureka-server项目,这里不需要添加eureka-server依赖,因为父工程中有了。

1. 添加配置

在resources目录中添加application.yml文件,在文件中添加配置

#服务端口号
server:
  port: 8100

spring:
  application:
    name: eureka-server
	
#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客户端与 Eureka 服务端进行交互的地址
      defaultZone: http://127.0.0.1:${server.port}/eureka
    #是否将自己注册到Eureka服务中,本身就是注册中心所以无需注册
    register-with-eureka: false
    #是否从Eureka中检索注册信息,本身就是注册中心所以无需检索
    fetch-registry: false
  server:
    # 测试时关闭自我保护机制,保证不可用服务及时踢出
    enable-self-preservation: false
    ##剔除失效服务间隔
    eviction-interval-timer-in-ms: 2000

2. 启动类

java目录下创建com.lxg二级目录,然后创建EurekaServerApp启动类

@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer.class, args);
    }
}

在类上加EnableEurekaServer注解,启动EurekaServer。

创建eureka客户端

以springcloud-eureka为父工程,新建springboot-eureka-client项目。在pom文件中添加依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
1. 添加配置

在resources目录中添加application.yml文件,在文件中添加配置

#端口号
server:
  port: 9100

spring:
  application:
    name: eureka-client

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客户端与 Eureka 服务端进行交互的地址
      defaultZone: http://127.0.0.1:8100/eureka/
  # 心跳检测检测与续约时间
  # 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
  instance:
    # Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则)
    lease-renewal-interval-in-seconds: 1
    # Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己)
    lease-expiration-duration-in-seconds: 2
2. 启动类

java目录下创建com.lxg二级目录,然后创建EurekaClientApp启动类,这里启动类名称不能为EurekaClient,否则会起冲突导致启动失败。类上使用EnableEurekaClient注解。

@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApp {
    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApp.class, args);
    }
}

测试

先启动服务端,再启动客户端,访问127.0.0.1:8100 ,访问监控页面。

图中的红色字体是在提示已经关闭了eureka的自我保护机制,此时如果关闭客户端,页面中的服务就会被踢出,如果没有关闭的话,即使客户端关闭了,服务依然会存在。

高可用注册中心(Eureka集群)

在微服务中,注册中心非常核心,可以实现服务治理,如果一旦注册出现故障的时候,可能会导致整个微服务无法访问,在这时候就需要对注册中心实现高可用集群模式。

Eureka高可用原理

默认情况下Eureka是让服务注册中心,不注册自己,但是在集群中,需要设置能注册自己,因为这两个属性默认为true,只需要不写就行了。

###使该注册中心注册自己
    register-with-eureka: true
###需要去注册中心上检索服务
    fetch-registry: true

Eureka高可用实际上将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组相互注册的服务注册中心,从而实现服务清单的互相同步,达到高可用效果。

Eureka集群环境搭建

新增server1,server2,server3三个节点。

server1配置:

#服务端口号
server:
  port: 8100

spring:
  application:
    name: eureka-server

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客户端与 Eureka 服务端进行交互的地址
      defaultZone: http://127.0.0.1:8200/eureka,http://127.0.0.1:8300/eureka
  server:
    # 测试时关闭自我保护机制,保证不可用服务及时踢出
    enable-self-preservation: false
    ##剔除失效服务间隔
    eviction-interval-timer-in-ms: 2000

server2配置:

#服务端口号
server:
  port: 8200

spring:
  application:
    name: eureka-server

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客户端与 Eureka 服务端进行交互的地址
      defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8300/eureka
  server:
    # 测试时关闭自我保护机制,保证不可用服务及时踢出
    enable-self-preservation: false
    ##剔除失效服务间隔
    eviction-interval-timer-in-ms: 2000

server3配置:

#服务端口号
server:
  port: 8300

spring:
  application:
    name: eureka-server

#eureka基本配置信息
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8200/eureka
  server:
    # 测试时关闭自我保护机制,保证不可用服务及时踢出
    enable-self-preservation: false
    ##剔除失效服务间隔
    eviction-interval-timer-in-ms: 2000

然后修改客户端的配置,因为以前是单个eureka注册中心,只需要注册进一个地址就行了,现在要注册进所有的注册中心。

#端口号
server:
  port: 9100

spring:
  application:
    name: eureka-client

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客户端与 Eureka 服务端进行交互的地址
      #单机
      #defaultZone: http://127.0.0.1:8100/eureka/
      #集群
      defaultZone: http://127.0.0.1:8100/eureka/,http://127.0.0.1:8200/eureka/,http://127.0.0.1:8300/eureka/
  # 心跳检测检测与续约时间
  # 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
  instance:
    # Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则)
    lease-renewal-interval-in-seconds: 1
    # Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己)
    lease-expiration-duration-in-seconds: 2

先启动所有的服务端节点,再启动客户端。访问localhost:8100 ,localhost:8200 ,localhost:8300 都能进入eureka的界面,同时看到客户端服务和其他的注册中心。这个时候即使有某一个节点挂了,服务依然是可用的,而且性能肯定比单机版要好。

搭建eureka集群有几点需要注意的:

1.与先前独立运行注册不同,注意defaultZone属性,它的值为除了自己以外的所有eureka节点的地址,以英文逗号分割。
2.去掉fetch-registry 与 register-with-eureka配置(其实这样做就会取对应的默认值,两个值均为true),需要让自己能被注册和检索。
3.启动第一个注册中心时会报Cannot execute request on any known server的错误,暂时不管它,实际上eureka注册中心的ui界面是能打开的,当所有的节点启动完毕,就能找到服务,此错误就会消失。
4.所有注册中心的节点的spring.application.name必须保持一致。
5.当客户端需要往注册中心集群注册服务时defaultZone属性需要把所有节点地址都加上,如果像单节点一样的话,你连接的那个节点挂了,集群中其他节点就无法获取到该服务,也就不能达到高可用。

本文GitHub源码:https://github.com/lixianguo5097/springcloud/tree/master/springcloud-eureka

CSDN:https://blog.csdn.net/qq_27682773
简书:https://www.jianshu.com/u/e99381e6886e
博客园:https://www.cnblogs.com/lixianguo

posted @ 2020-03-19 10:33  小小的bug  阅读(223)  评论(0编辑  收藏  举报