Spring Cloud(二):Eureka

服务发现概述

  • 服务发现机制是为了解决硬网络编码问题,服务消费者使用这种机制获取服务提供者网络信息,当微服务网络地址发生变更(例如IP或端口),会重新注册到服务发现组件,而服务消费者就无须人工修改网络地址了。
  • 服务提供者、服务消费者、服务发现之间的关系如下:
    • 微服务在启动时,将自己的网络地址等信息注入到服务发现组件中,服务发现组件会存储这些信息
    • 服务消费者可以从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务提供者的接口
    • 各个微服务与服务发现组件使用一定的机制(心跳)通信,服务组件如长时间无法与某微服务实例通信,就会注销该实例
  • 服务发现组件功能:
    • 服务注册表:是服务发现组件的核心,用来记录微服务信息,如名称、IP、端口等,提供查询和管理API,注册和注销
    • 服务注册与服务发现:服务注册是指服务在启动时将自己信息发送到服务注册组件上,服务发现是指查询可用微服务列表及网络地址的机制
    • 服务检查:服务发现组件检测已注册的服务,若某一个服务长时间无法访问则移除该实例

Eureka

  • Eureka 是开源的服务发现组件,本身是一个基于 REST 的服务,包含 Server 和 Client 两部分
Eureka 架构
  • Region:表示服务系统中的地理位置,Spring Cloud默认使用的是 us-east-1
  • Availbility Zone:可理解为机房,Region理解为跨机房的 Eureka 集群
  • Application Service:服务提供者
  • Application Client:服务消费者
  • Make Remote Call:如调用 RESTful API
  • us-east-1c、us-east-1d、us-east-1e都是zone,都属于 us-east-1 这个region
  • Eureka Server:提供服务发现能力,各个微服务启动时,会向 Eureka Server 注册自己的信息,Eureka Server 会保存这些信息
  • Eureka Client:是一个 Java 客户端,用于简化与Eureka Server的交互
  • 微服务启动后,会周期性(默认30秒)的与 Eureka Server 发送心跳信息
  • 如果 Eureka Server 在一定时间内没接收到心跳信息则注销该实例(默认90秒)
  • 多个 Eureka Server 之间通过复制方式,来实现服务注册表中数据的同步
  • Eureka Client 会缓存注册表中信息,微服务无需每次请求都查询 Eureka Server,降低了压力

快速入门

编写 Eureka Server (Demo源码 eureka)

  • 引入 Eureka Server 库
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
  • 添加 @EnableEurekaServer注解声明这是一个 Eureka Server
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}
  • 添加配置
server:
  port: 8761                  #配置端口
eureka:
  client:
    register-with-eureka: false   #是否向服务端注册自己,它本身就是Eureka Server,所以为false
    fetch-registry: false         #表示是否从Eureka Server获取信息,因为这是一个单节点,不需要同步其它Eureka Server的数据,所以为 false
    service-url:
      defaultZone: http://localhost:8761/eureka/    #设置 Eureka Client 与 Eureka Server 同步的地址,注册、查询服务都要使用该地址,多个地址可用逗号分隔

将微服务注册到 Eureka Server

  • 引入 Eureka Client 库
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>
  • 添加 @EnableDiscoverClient 注解声明这是一个微服务
@SpringBootApplication
@EnableEurekaClient
public class FlimUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(FlimUserApplication.class, args);
    }
}
  • 添加配置
spring:
  application:
    name: flim-user   #注册到Eureka的名字
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/    #设置EurekaServer的服务地址
  instance:
    prefer-ip-address: true   #true代表将自己的IP注册到EurekaServer上,false代表注册主机名到EurekaServer
image

Eureka高可用

  • 单节点 Eureka Server 并不适合生产环境,Eureka Client 会定时连接 Eureka Server,获取服务注册表中的信息并缓存在本地,微服务消费远程 API 时总是使用本地缓存中的数据。因此即使 Eureka Server 发生故障也不会影响到服务之间的调用,但如果 Eureka Server 故障时,某些微服务也出现了不可用的情况,Eureka Client 中的缓存若不被更新,则可能会影响到微服务之间的调用
  • 因此在生产环境中,通常会部署一个高可用的 Eureka Server 集群,Eureka Server 可以通过运行多个实例并相互注册的方式实现高可用部署,Eureka Server 之间也会彼此增量的同步信息,从而确保所有节点数据一致,节点之间相互注册也是 Eureka Server 的默认行为

构建双结点的 Eureka Server 集群

  • 修改 Eureka Server 配置文件,添加两个 Eureka 环境
spring:
  application:
    name: eureka
---
spring:
  profiles: eureka1
eureka:
  instance:
    hostname: eureka1
  client:
    service-url:
      default-zone: http://localhost:8762/eureka
---
spring:
  profiles: eureka2
eureka:
  instance:
    hostname: eureka2
  client:
    service-url:
      default-zone: http://localhost:8761/eureka
  • 执行以下命令启动两个 Eureka Server 实例,可看到两个实例之间已同步(注意:先后运行实例后,要等待一会才会同步成功)
java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active = eureka1 --server.port=8761
java -jar eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active = eureka2 --server.port=8762
Eureka 高可用

将应用注册到 Eureka Server 集群上

  • 只需配置多个 Eureka Server 地址,就可以将其注册到Eureka Server集群了
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
  • 即使只配置一个 Eureka Server 集群中的某个节点,也能注册到集群上,因为多个Eureka Server之间数据会同步

Eureka Server 安全

为 Eureka Server 添加用户认证

  • 前面例子中 Eureka Server 允许匿名访问,可以构建一个需要登录才能访问的 Eureka Server,这需要添加 Spring Security 库
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  • 添加以下配置
spring:
    security:
      user:
        name: user
        password: 123456
  • 访问 http:localhost:8761/ 时会提示身份验证对话框

将微服务注册到需要认证的 Eureka Server

  • 修改配置文件,属性上加上用户名与密码即可
eureka:
  client:
    serviceUrl:
      defaultZone: http://user:123456@localhost:8761/eureka/
  • 对于更复杂的需求可以创建一个类型为 DiscoveryClientOptionalArgs 的 @Bean,并向其中注入 ClientFilter

Eureka 元数据

  • Eureka 元数据有两种,标准元数据和自定义元数据
    • 标准元数据:指主机名、IP地址、端口号和健康检查信息等,这些信息都会被发现服务注册表中,用于服务之间调用
    • 自定义元数据:可以用 eureka.instance.metadata-map 配置,这些元数据可以在远程客户端中访问,但一般不会改变远程客户端行为,除非客户端知道该元数据的含义

自定义元数据-服务提供者

  • 修改配置文件,使用 eureka.instance.metadata-map 添加自定义元数据
eureka:
  client:
    serviceUrl:
      defaultZone: http://user:123456@localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    metadata-map:
      my-metadata: 自定义的元数据

自定义元数据-服务消费者

  • 修改电影微服务 MovieController 获取自定义元数据
@RestController
public class MovieController {
    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/user-instance")
    public List<ServiceInstance> showInfo(){
        return this.discoveryClient.getInstances("flim-user");      //获取Flim-User微服务实例的信息
    }
}
[
    {
        "host": "192.168.43.43",
        "port": 8080,
        "metadata": {
            "management.port": "8080",
            "my-metadata": "自定义的元数据"
        },
        "secure": false,
        "serviceId": "FLIM-USER",
        "uri": "http://192.168.43.43:8080",
        "instanceInfo": {
            "instanceId": "linyuandembp:flim-user",
            "app": "FLIM-USER",
            "appGroupName": null,
            "ipAddr": "192.168.43.43",
            "sid": "na",
            "homePageUrl": "http://192.168.43.43:8080/",
            "statusPageUrl": "http://192.168.43.43:8080/info",
            "healthCheckUrl": "http://192.168.43.43:8080/health",
            "secureHealthCheckUrl": null,
            "vipAddress": "flim-user",
            "secureVipAddress": "flim-user",
            "countryId": 1,
            "dataCenterInfo": {
                "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                "name": "MyOwn"
            },
            "hostName": "192.168.43.43",
            "status": "UP",
            "leaseInfo": {
                "renewalIntervalInSecs": 30,
                "durationInSecs": 90,
                "registrationTimestamp": 1513418181530,
                "lastRenewalTimestamp": 1513418181530,
                "evictionTimestamp": 0,
                "serviceUpTimestamp": 1513418181530
            },
            "isCoordinatingDiscoveryServer": false,
            "metadata": {
                "management.port": "8080",
                "my-metadata": "自定义的元数据"
            },
            "lastUpdatedTimestamp": 1513418181530,
            "lastDirtyTimestamp": 1513418181485,
            "actionType": "ADDED",
            "asgName": null,
            "overriddenStatus": "UNKNOWN"
        }
    }
]

Eureka Server 的 REST 端点

  • Eureka Server 提供了一些 REST 端点,非 JVM 的微服务可以使用这些 REST 端点操作 Eureka,从而实现服务注册与发现,其中:
    • appID:应用程序的名称
    • instanceID:与实例相关联的唯一ID
REST 端点集合
  • 使用 REST 请求向 Eureka Server 注册微服务时,需要 POST 一个请求体( JSON 或 XML 格式,请求题略长,此处不展示

Eureka 自我保护模式

  • Eureka 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.
  • 默认情况下,如果 EurekaServer 在一定时间内没接收到某个微服务的心跳,则会注销该实例,但当网络发生故障,微服务无法与 EurekaServer 正常通信时,该行为就非常危险了,Eureka 通过“自我保护”来解决这个问题,当 Eureka Server 节点短时间内丢失过多客户端时,就会进入自我保护模式,一旦进入该模式,Eureka Server 就会保护服务注册表中的信息,不再删除服务注册表中的信息(不注销任何微服务),当网络恢复后,Eureka Server 退出自我保护模式

多网环境下的IP选择

  • 对于多网卡用户,如某台服务器有三块网卡,但只有 eth1 可以被访问,可以通过配置文件来实现
  • 使用 spring.cloud.inetutils.ignored-interfaces 属性忽略指定名称的网卡
spring:
  cloud:
    inetutils:
      ignored-interfaces:
        - docker0     #忽略docker0网卡
        - veth.*      #忽略所有veth开头的网卡
  • 使用 spring.cloud.inetutils.preferredNetworks 属性指定使用的网络地址
spring:
  cloud:
    inetutils:
      preferred-networks:
        - 192.168
        - 10.0      

Eureka 健康检查

  • 在 Status 栏有个 UP 表示应用程序状态正常,还有其它值如:DOWN、OUT_OF_SERVICE、UNKNOWN等,但只有 UP 状态的才会被服务请求,Eureka Server 与 Eureka Client 之间使用心跳机制确定 Eureka Client 状态,正常情况下为 UP
  • 该机制不能反应应用程序状态,如微服务与 Eureka Server 心跳正常,可该服务数据源发生问题,无法正常工作
  • Spring Boot Actuator 提供了 /health 端点,该端点可展示应用程序健康信息

Eureka 常见问题

Eureka 注册服务慢

  • 默认情况下,服务注册到 Eureka Server 的过程较慢,在开发测试时往往希望能快速一点,从而提升工作效率
  • 服务的注册涉及到周期性心跳,默认 30 秒一次,只有当实例、服务器端和客户端的本地缓存中的元数据都相同时,服务才能被发现(所以可能需要 3 次心跳)
  • 可以使用参数 eureka.instance.leaseRenewalintervalInSeconds 修改时间间隔,单位为秒,从而加快客户端连接到服务的过程
    -生产环境最好使用默认值,因为服务器内部有一些计算

已停止的微服务节点注销慢或不注销

  • 开发环境下,我们希望能迅速有效的注销已停止的微服务实例,但 Eureka Server 清理无效节点周期(默认 90 秒),以及自我保护等原因,可能回遇到微服务注销慢甚至不注销
  • 解决方法如下:
    • Eureka Server 端:
      • 关闭自我保护,并配置清理无效节点的间隔
      eureka.server.enable-self-preservation = false  #关闭自我保护
      eureka.server.eviction-interval-timer-in-ms = 60000 #清理间隔,单位毫秒
      
    • Eureka Client 端:
      • 开启健康检查,并设置更新时间和到期时间
      eureka.client.healthcheck.enabled = true  #开启健康检查
      eureka.instance.lease-renewal-interval-in-seconds = 30000  #更新时间,单位毫秒,默认30秒
      eureka.instance.lease-expiration-duration-in-seconds = 90000  #到期时间,单位毫秒,默认90秒
      

如何自定义微服务的 InstanceID

  • InstanceID 用于唯一标识到 Eureka Server 上的微服务实例,Eureka Server 首页可以直观的看到各个微服务的 InstanceID
  • 在 Spring Cloud 中,服务的 InstanceID 默认值是
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
  • 也可以通过下面配置修改 InstanceID 格式
eureka:
    instance:
        instance-id: ${spring.cloud.client.ipAddress}:${server.port}

Eureka 的UNKNOWN 问题

  • 注册信息 UNKNOWN 有两种情况,一种是应用名称 UNKNOWN,一种是应用状态 UNKNOWN
  • 应用名称 UNKNOWN 可能是因为未配置 spring.application.name 或 eureka.instance.appname 属性
  • 应用状态 UNKNOWN 是微服务不正常情况,该问题一般由健康检查导致,eureka.client.healthcheck.enabled=true 必须设置在 application.yml 中,而不是 bootstrap.yml 中
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容