2019-07-07

Eureka简单学习

这是个人学习笔记,如有错误,还望海涵、斧正。

一、简介

Eureka 是一个专门用于服务发现的服务器,一些服务注册到该服务器,而另一些服务通过该服务器查找其所要调用执行的服务。

其本身是一个基于 REST 的服务,主要用于定位运行在 AWS 域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。

可以充当服务发现服务器的组件很多,例如 Zookeeper、Consul、Eureka 等。

下图是一张Eureka的体系架构:

eureka_architecture.png

(图片引自:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance

二、使用方法

通过idea的Spring Initializr创建一个springcloud项目

创建步骤1.png

1.创建server

(1)导入Eureka Server依赖

创建步骤2.png

(2)修改application配置文件为yml格式

​ 直接改后缀名就行(个人习惯,yml更舒服)

(3)修改配置文件

eureka:
  instance:
    hostname: localhost  #指定Eureka主机
  client:
    register-with-eureka: false  #是否向Eureka注册自己(主机不注册自己)
    fetch-registry: false  #指定此客户端是否可以获取eureka的注册信息
    service-url: #暴露服务中心地址
        defaultZone: http://localhost:8081/eureka
server:
  port: 8081

(4)启动

给启动类加上 开启Eureka的注解 @EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class TigerApplication {

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

}

启动项目,访问项目地址http://localhost:8081/,成功启动后如下图

server启动.png

2.创建provider

有两种方式供选择:

第一种方式

引用依赖

嫌麻烦就直接复制刚刚创建的server项目,重命名为eureka-provider-8082。然后手动修改pom文件(复制粘贴下面的的依赖就行)

*删掉删掉删掉删掉原来的server依赖,就是下面这个:

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

然后引入一个client的依赖:

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

provider引入上面两个依赖就够了,但是为了和数据库做连接,我们还要引入下面的依赖:

     <!--数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--修改MySQL驱动版本-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>
        <!-- jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

除了数据库,作为一个web项目还要引入以下依赖:

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

下面两个是辅助用的依赖,可引可不引。lombok用来生成实体类的get/set方法,actuator用来配置监控终端的显示信息

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
         <!--actuator 依赖-->
        <dependency> <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
配置

配置文件(info为actuator配置)

eureka:
  instance:
    hostname: localhost #指定Eureka主机
    instance-id: eureka-provider-8082 #指定当前客户端在服务中心注册的名称

  # 指定服务中心
  client:
    # register-with-eureka: true #是否向Eureka注册自己(主机不注册自己)
    # fetch-registry: true #指定此客户端是否可以获取eureka的注册信息
    service-url: #暴露服务中心地址
        defaultZone: http://localhost:8081/eureka
server:
  port: 8082

spring:
  # 指定当前微服务对外暴露的名称
  application:
    name: eureka-provider-1
  # 配置spring-data-jap
  jpa:
   # 是否在spring容器启时创建表
    generate-ddl: true
    show-sql: true
    hibernate:
      ddl-auto: none


 # 配置数据源
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///test_my?useUnicode=true&amp;characterEncoding=utf8
    username: root
    password: root

logging:
  # 设置日志输出格式
  pattern:
    console: level-%level %msg%n
  level:
    root: info
    org.hibernate: info
    org.hibernate.type.descriptor.sql.BasicBinder: trace
    org.hibernate.type.descriptor.sql.BasicExtractor: trace
    com.abc.provider: debug

# Eureka工作界面中instance-id处链接的信息 参数都是自定义的
info:
    author: tiger
    app.name: eureka-provider-8082
    app.desc: a desc





第二种方式

重新创建一个项目,在依赖处选择照下图选择就行

clientconfig.png
配置

配置文件:

由于数据库连接驱动不同,所以datasource配置和第一种方法的不太一样。其他配置则一致

eureka:
  instance:
    hostname: localhost #指定Eureka主机
    instance-id: eureka-provider-8082 #指定当前客户端在服务中心注册的名称

  # 指定服务中心
  client:
    # register-with-eureka: true #是否向Eureka注册自己(主机不注册自己)
    # fetch-registry: true #指定此客户端是否可以获取eureka的注册信息
    service-url: #暴露服务中心地址
        defaultZone: http://localhost:8081/eureka
server:
  port: 8083

spring:
  # 指定当前微服务对外暴露的名称
  application:
    name: eureka-provider-1
  # 配置spring-data-jap
  jpa:
   # 是否在spring容器启时创建表
    generate-ddl: true
    show-sql: true
    hibernate:
      ddl-auto: none


 # 配置数据源
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///test_my?useUnicode=true&amp&characterEncoding=utf8&serverTimezone=GMT
    username: root
    password: root

logging:
  # 设置日志输出格式
  pattern:
    console: level-%level %msg%n
  level:
    root: info
    org.hibernate: info
    org.hibernate.type.descriptor.sql.BasicBinder: trace
    org.hibernate.type.descriptor.sql.BasicExtractor: trace
    com.abc.provider: debug

# Eureka工作界面中instance-id处链接的信息 参数都是自定义的
info:
    author: tiger
    app.name: eureka-provider-8082
    app.desc: a desc





启动

在启动类上加上启动注解@EnableEurekaClient,项目跑起来后在 http://localhost:8081/ 上可以看到已经注册进去了

注册provider.png

至于controller service dao entity 这些,相信大家都会的,这里就不写出来了。

3.创建consumer

依赖

consumer依赖.png

配置

eureka:
  instance:
    hostname: localhost #指定Eureka主机
    instance-id: eureka-consumer-8083 #指定当前客户端在服务中心注册的名称

  # 指定服务中心
  client:
    # register-with-eureka: true #是否向Eureka注册自己(主机不注册自己)
    # fetch-registry: true #指定此客户端是否可以获取eureka的注册信息
    service-url: #暴露服务中心地址
        defaultZone: http://localhost:8081/eureka
server:
  port: 8083

spring:
  # 指定当前微服务对外暴露的名称
  application:
    name: eureka-consumer-1


# Eureka工作界面中instance-id处链接的信息 参数都是自定义的
info:
    author: tiger
    app.name: eureka-provider-8082
    app.desc: a desc

启动

启动类加上注解@EnableEurekaClient,启动完成后在 http://localhost:8081/ 上可以看到已经注册进去了

注册成功consumer.png

4.创建集群

由于都使用同一个机器进行测试,先配一下host文件,加入以下配置

#eureka server 
127.0.0.1  eureka-server-8081 
127.0.0.1  eureka-server-8088
127.0.0.1  eureka-server-8089

然后将之前端口为8081的eureka-server项目复制两份,共三个项目组成集群

三个项目的yml文件配置如下:

eureka-server-8081:

eureka:
  instance:
    hostname: eureka-server-8081  #指定Eureka主机
  client:
    register-with-eureka: false  #是否向Eureka注册自己(主机不注册自己)
    fetch-registry: false  #指定此客户端是否可以获取eureka的注册信息
    service-url: #暴露服务中心地址
        defaultZone: http://eureka-server-8081:8081/eureka,http://eureka-server-8088:8088/eureka,http://eureka-server-8089:8089/eureka
server:
  port: 8081

eureka-server-8088:

eureka:
  instance:
    hostname: eureka-server-8088  #指定Eureka主机
  client:
    register-with-eureka: false  #是否向Eureka注册自己(主机不注册自己)
    fetch-registry: false  #指定此客户端是否可以获取eureka的注册信息
    service-url: #暴露服务中心地址
        defaultZone: http://eureka-server-8081:8081/eureka,http://eureka-server-8088:8088/eureka,http://eureka-server-8089:8089/eureka
server:
  port: 8088

eureka-server-8089:

eureka:
  instance:
    hostname: eureka-server-8089  #指定Eureka主机
  client:
    register-with-eureka: false  #是否向Eureka注册自己(主机不注册自己)
    fetch-registry: false  #指定此客户端是否可以获取eureka的注册信息
    service-url: #暴露服务中心地址
        defaultZone: http://eureka-server-8081:8081/eureka,http://eureka-server-8088:8088/eureka,http://eureka-server-8089:8089/eureka
server:
  port: 8089

可以看到,要组成集群,则将所有的server的地址都配置到defaultZone上即可,每个地址通过逗号分隔(注意逗号不能有空格,因为yml里空格是有效字符)

然后分别启动三个项目,得出以下界面,即为集群搭建成功:

http://eureka-server-8081:8081/

集群1.png

http://eureka-server-8088:8088/

集群2.png

http://eureka-server-8089:8089/

集群3.png

这时候启动 provider 和 consumer 的微服务,只要 provider 和 consumer 的 defaultZone 中配了上面任意一个server 的地址,就可以在三个 server 中都注册。

当然,provider 和 consumer 最好将三个地址都配上,这样某个 server 挂掉后, 还能继续使用其他 server

启动 provider 和 consumer 后,我们查看三个 server 的界面,他们都会存在同样的服务列表如下:

服务列表.png

三、底层原理

Eureka 注册信息管理机制

描述

Eureka 的注册信息则是通过“缓存 + 存储”的双 Map 机制完成的。

Eureka 的数据存储分了两层:数据存储层和数据缓存层。
Eureka Client 在拉取服务信息时,先从缓存层获取,若获取不到,则先把数据存储层的数据加载到缓存中,再从缓存中获取。(也就是说,Eureka Client永远都是从缓存层拉取服务信息)

数据存储层

数据存放位置

数据存储层中的信息是存放在内存中的,不做持久化。

原因:

1.若不是所有 Eureka Server 都宕机,则宕机的 Server 重启后,会从其它 Server 中同步注册信息

2.若全部 Eureka Server 宕机,则重新接受客户端的注册

数据存放方式

用于存储注册信息的是一个双层 ConcurrentHashMap。如:Map<String, Map<String, Lease>>

第一层的 key 是 spring.application.name

第二层的 key 是微服务主机的 InstanceId(即配置文件里的 instance-id),value 是 Lease 对象(续租对象)。Lease 对象包含了服务详情和服务治理相关的信息。

当出现客户端向 Eureka 服务器提交 register、renew 和 cancel 请求时,都会修改这 个双层 Map 中的数据。

数据缓存层

一级缓存 readOnlyCacheMap

一级缓存中的数据只要不被替换就会一直存在,客户端拉取注册信息时就是从这个缓存中拉取

二级缓存 readWriteCacheMap

二级缓存中的数据会随着客户端的 register、renew 和 cancel 请求而更新,因为当客户端发起这些请求,数据存储层的数据会被更新,为了保证缓存层和存储层数据的一致性,二级缓存会从存储层从新加载数据。

新数据的加载不会立即引发一级缓存的更新或清空,一级缓存会定期从二级缓存中同步数据。

Eureka 的自我保护机制

现象

在上面启动 provider 和 consumer 项目的时候,我们都能在 server 的界面上看到一句红色的警告

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 不能够正确判断它们是否处于 up 状态。当更新(指收到的微服务主机的心跳)小于阈值时,为了安全,微服务主机将不再失效。

原因

默认情况下,Eureka Server 在 90 秒内(微服务默认30秒发一次心跳)没有收到某微服务的心跳,则判断改为服务的主机宕机,将该微服务从服务注册信息表中删除。

但很多时候,因为网络抖动等原因,导致微服务主机发送的心跳没有被 Eureka Server 收到,从而被 Eureka Server 删除。

若短时间内网络恢复正常,但由于服务列表中删除了该微服务,所以该微服务不能再提供服务。

为了防止上述情况发生,Eureka 便有了自我保护机制。

即在短时间内,如果 Eureka Server 丢失了较多的服务,即收到的心跳数量小于阈值,为了保证系统的可用性,Eureka会进入自我保护模式,此时服务列表只可以读取、注册,不可以删除。这样便给了那些因为网络抖动而被Eureka Server 误认为宕机的微服务主机重新复活的机会。当网络恢复正常,微服务主机得以继续发送心跳,当心跳数达到阈值以上,则退出自我保护模式。

关于阈值:自我保护机制的默认阈值是85%。当一定时间内收到的心跳数小于总微服务数量的85%时,开启自我保护模式。

从页面上的两个参数,我们可以判断出来什么时候会开启自我保护

自我保护说明.png

Renews threshold 是 15分钟内每分钟应该收到的数量。

Renews (last min) 是 最后一分钟实际收到的数量。

注意,Eureka默认你的是微服务应用是100个,所以(100*85%)/15 取整后为 5,即每分钟应该收到5个以上的心跳。

所以,很容易判断,当Renews (last min) < Renews threshold时,自我保护就会开启。

但由上我们又知道,如果注册到 Eureka 的 微服务不达到一定的数量,这个自我保护机制并不能由明显效果。

下面给出自定义自我保护阈值和关闭自我保护的参数:

eureka:
  server:
    renewal-percent-threshold: 0.75 #默认0.85
    enable-self-preservation: true # 关闭自我保护机制
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容

  • 1 为什么需要服务发现 简单来说,服务化的核心就是将传统的一站式应用根据业务拆分成一个一个的服务,而微服务在这个基...
    谦小易阅读 25,093评论 4 93
  • 当我们使用一项新的技术时, 我们通常应该先思考一个问题:为什么要使用这项技术, 或者说这项技术能解决什么问题, 这...
    0d1b415a365b阅读 665评论 0 2
  • Demo 源码下载 本案例为源码分支的 eureka 分支 服务发现概述 服务发现机制是为了解决硬网络编码问题,服...
    聪明的奇瑞阅读 5,189评论 0 13
  • 一、Eureka的基础架构 Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现...
    努力努力再努力_y阅读 919评论 0 0
  • 3月7号上线的app,先撸为敬 1.扫二维码或者通过链接地址进入 注册地址:https://www.flowdmc...
    ea1723d66189阅读 344评论 0 1