11. 服务发现:Eureka Clients

Service Discovery 是基于微服务的 architecture 的 key 原则之一。尝试手动配置每个 client 或某种形式的约定可能非常难以做到并且可能非常脆弱。 Eureka 是 Netflix 服务发现服务器和 Client。可以配置和部署服务器以使其具有高可用性,每个服务器将注册服务的 state 复制到其他服务器。

11.1 如何包含 Eureka Client

要在项目中包含 Eureka Client,请使用带有 group org.springframework.cloud和 artifact id spring-cloud-starter-netflix-eureka-client的 starter。有关使用当前 Spring Cloud Release Train 设置 build 系统的详细信息,请参阅Spring Cloud 项目页面

11.2 注册 Eureka

当 client 向 Eureka 注册时,它提供 meta-data 自身,例如 host 和 port,运行状况指示器 URL,主页等.Eureka 从属于服务的每个实例接收心跳消息。如果心跳故障超过可配置的时间表,则通常会从注册表中删除该实例。

Example eureka client:

@Configuration
@ComponentScan
@EnableAutoConfiguration
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello world";
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

(i.e.完全正常 Spring Boot 应用程序)。通过 class 路径上的spring-cloud-starter-netflix-eureka-client,您的 application 将自动注册 Eureka 服务器。 Configuration 是定位 Eureka 服务器所必需的。 例:

application.yml.

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

其中“defaultZone”是一个 magic string fallback value,它为任何不表达首选项的 client 提供服务 URL(i.e.它是一个有用的默认值)。

Environment获取的默认 application name(服务 ID),virtual host 和 non-secure port 分别是${spring.application.name}${spring.application.name}${server.port}

在 classpath 上有spring-cloud-starter-netflix-eureka-client使得 app 成为 Eureka“实例”(i.e.它自己注册)和“client”(i.e.它可以查询注册表以查找其他服务)。实例行为由eureka.instance.* configuration 键驱动,但是如果确保 application 具有spring.application.name(这是 Eureka 服务 ID 或 VIP 的默认值),则默认值将正常。

有关可配置选项的更多详细信息,请参见EurekaInstanceConfigBeanEurekaClientConfigBean

要禁用 Eureka Discovery Client,您可以将eureka.client.enabled设置为false

11.3 使用 Eureka Server 进行身份验证

如果其中一个eureka.client.serviceUrl.defaultZone URL 中嵌入了凭据(卷曲样式,如http://user:[email protected]:8761/eureka),则 HTTP 基本身份验证将自动添加到 eureka client。对于更复杂的需求,您可以在其中创建@Bean类型的DiscoveryClientOptionalArgs和 inject ClientFilter实例,所有这些实例都将应用于 calls 从 client 到服务器。

由于 Eureka 中存在限制,因此无法支持 per-server 基本身份验证凭据,因此仅使用找到的第一个集合。

11.4 状态页面和健康指标

Eureka 实例的状态页面和运行状况指示器分别默认为“/info”和“/health”,它们是 Spring Boot Actuator application 中有用 endpoints 的默认位置。如果使用 non-default context 路径或 servlet 路径(e.g. server.servletPath=/foo),则需要更改这些,即使对于 Actuator application 也是如此示例:

application.yml.

eureka:
  instance:
    statusPageUrlPath: ${server.servletPath}/info
    healthCheckUrlPath: ${server.servletPath}/health

这些链接显示在 clients 消耗的元数据中,并在某些情况下用于决定是否向 application 发送请求,因此如果它们准确,则会有所帮助。

在 Dalston 中,还需要在更改 management context 路径时设置状态和运行状况检查 URL。在 Edgware 中删除了此要求。

11.5 注册安全 Application

如果您希望通过 HTTPS 联系您的应用,则可以分别在EurekaInstanceConfig,即eureka.instance.[nonSecurePortEnabled,securePortEnabled]=[false,true]中设置两个标记。这将使 Eureka 发布实例信息,显示对安全通信的明确偏好。对于以这种方式配置的服务,Spring Cloud DiscoveryClient将始终 return 以https开头的 URI,Eureka(本机)实例信息将具有安全的运行状况检查 URL。

由于 Eureka 在内部工作的方式,它仍将发布状态和主页的 non-secure URL,除非您也明确覆盖这些 URL。您可以使用占位符来配置 eureka 实例 URL,e.g.

application.yml.

eureka:
  instance:
    statusPageUrl: https://${eureka.hostname}/info
    healthCheckUrl: https://${eureka.hostname}/health
    homePageUrl: https://${eureka.hostname}/

(请注意,${eureka.hostname}是仅在 Eureka 的更高版本中可用的本机占位符.您也可以使用 Spring 占位符实现相同的功能,e.g. 使用${eureka.instance.hostName} .)

如果您的应用程序在代理后运行,并且 SSL 终止在代理中(e.g. 如果您在 Cloud Foundry 或其他平台中作为服务运行),则需要确保代理“转发”headers 被拦截和处理通过 application。 Spring Boot 应用程序中的嵌入式 Tomcat 容器如果具有'X-Forwarded - *`headers 的显式 configuration,则会自动执行此操作。这个错误的标志是你的应用程序呈现给自己的链接是错误的(错误的 host,port 或协议)。

11.6 Eureka 的健康检查

默认情况下,Eureka 使用 client 心跳来确定 client 是否已启动。除非另有说明,否则 Discovery Client 不会传播 Spring Boot Actuator 的 application 的当前运行状况检查状态。这意味着在成功注册后 Eureka 将始终宣布 application 处于'UP'state 状态。通过启用 Eureka 运行状况检查可以更改此行为,从而将 application 状态传播到 Eureka。因此,除了'UP'之外,其他每个 application 都不会向 state 中的 application 发送流量。

application.yml.

eureka:
  client:
    healthcheck:
      enabled: true

eureka.client.healthcheck.enabled=true只应在application.yml中设置。在bootstrap.yml中设置 value 会导致不良副作用,例如在 eureka 中注册UNKNOWN状态。

如果您需要对健康检查进行更多控制,可以考虑实施自己的com.netflix.appinfo.HealthCheckHandler

实例和 Clients 的 11.7 Eureka 元数据

值得花一点时间了解 Eureka 元数据的工作方式,因此您可以在平台中使用它。有关于主机名,IP 地址,port numbers,状态页和运行状况检查等内容的标准元数据。这些发布在服务注册表中,并由 clients 用于以直接的方式联系服务。可以将其他元数据添加到eureka.instance.metadataMap中的实例注册中,这可以在 remote clients 中访问,但通常不会更改 client 的行为,除非它了解元数据的含义。下面介绍了几个特殊情况,其中 Spring Cloud 已经为元数据 map 赋予了意义。

11.7.1 在 Cloud Foundry 上使用 Eureka

Cloud Foundry 有一个 global router,以便同一个应用程序的所有实例具有相同的主机名(在具有类似 architecture 的其他 PaaS 解决方案中也是如此)。这不一定是使用 Eureka 的障碍,但是如果你使用 router(推荐,甚至是强制性的,取决于你的平台的设置方式),你需要显式设置主机名和 port numbers(安全或 non-secure)所以他们使用 router。您可能还想使用实例元数据,以便区分 client 上的实例(自定义负载均衡器中的 e.g. )。默认情况下,eureka.instance.instanceIdvcap.application.instance_id。例如:

application.yml.

eureka:
  instance:
    hostname: ${vcap.application.uris[0]}
    nonSecurePort: 80

根据在 Cloud Foundry 实例中设置安全规则的方式,您可以注册并使用 host VM 的 IP 地址直接 service-to-service calls。 Pivotal Web Services(PWS)上尚未提供此 feature。

11.7.2 在 AWS 上使用 Eureka

如果计划将 application 部署到 AWS 云,则必须将 Eureka 实例配置为支持 AWS,这可以通过以下方式自定义EurekaInstanceConfigBean来完成:

@Bean
@Profile("!default")
public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
  EurekaInstanceConfigBean b = new EurekaInstanceConfigBean(inetUtils);
  AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
  b.setDataCenterInfo(info);
  return b;
}

11.7.3 更改 Eureka 实例 ID

一个 vanilla Netflix Eureka 实例注册的 ID 等于其 host name(i.e.每个 host 只有一个服务)。 Spring Cloud Eureka 提供了一个合理的默认值,如下所示:${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}。对于 example myhost:myappname:8080

使用 Spring Cloud,您可以通过在eureka.instance.instanceId中提供唯一标识符来覆盖它。例如:

application.yml.

eureka:
  instance:
    instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

有了这个元数据,并且在 localhost 上部署了多个服务实例,随机 value 将在那里启动以使该实例唯一。在 Cloud Foundry 中,vcap.application.instance_id将在 Spring Boot application 中自动填充,因此不需要随机 value。

11.8 使用 EurekaClient

一旦您拥有了一个发现客户端的应用程序,您就可以使用它来发现Eureka Server中的服务实例。一种方法是使用原生com.netflix.discovery.EurekaClient(而不是 Spring Cloud DiscoveryClient),e.g.

@Autowired
private EurekaClient discoveryClient;

public String serviceUrl() {
    InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
    return instance.getHomePageUrl();
}

不要在@PostConstruct方法或@Scheduled方法中使用EurekaClient(或者可能尚未启动的任何地方)。它在SmartLifecycle(带有phase=0)中初始化,因此最早可以依赖它可用的是另一个具有更高相位的SmartLifecycle

11.8.1 没有 Jersey 的 EurekaClient

默认情况下,EurekaClient 使用 Jersey 进行 HTTP 通信。如果您希望避免来自 Jersey 的依赖项,则可以将其从依赖项中排除。 Spring Cloud 将根据 Spring RestTemplate自动配置 transport client。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jersey.contribs</groupId>
            <artifactId>jersey-apache-client4</artifactId>
        </exclusion>
    </exclusions>
</dependency>

11.9 原生 Netflix EurekaClient 的替代品

您不必使用原始 Netflix EurekaClient,通常在某种 wrapper 后面使用它会更方便。 Spring Cloud 支持假装(REST 客户端构建器),并且Spring RestTemplate使用逻辑 Eureka 服务标识符(VIP)而不是物理 URL。要使用固定的物理服务器列表配置 Ribbon,您只需将<client>.ribbon.listOfServers设置为 comma-separated 物理地址(或主机名)列表,其中<client>是 client 的 ID。

您还可以使用org.springframework.cloud.client.discovery.DiscoveryClient为发现客户端提供一个简单的 API,该 API 不是特定于 Netflix,e.g.

@Autowired
private DiscoveryClient discoveryClient;

public String serviceUrl() {
    List<ServiceInstance> list = discoveryClient.getInstances("STORES");
    if (list != null && list.size() > 0 ) {
        return list.get(0).getUri();
    }
    return null;
}

11.10 为什么注册服务这么慢?

作为一个实例还涉及到注册表的周期性心跳(通过 client 的serviceUrl),默认持续时间为 30 秒。服务器不可供 clients 发现,直到实例,服务器和 client 在其本地缓存中都具有相同的元数据(因此它可能需要 3 个心跳)。您可以使用eureka.instance.leaseRenewalIntervalInSeconds更改周期,这将加速 process 连接到其他服务的 process。在 production 中,最好坚持使用默认值,因为服务器内部有一些计算可以对租约续订期做出假设。

11.11 区域

如果您已将 Eureka clients 部署到多个 zones,那么您可能希望这些 clients 在另一个 zone 中尝试服务之前利用同一 zone 内的服务。为此,您需要正确配置 Eureka clients。

首先,您需要确保将 Eureka 服务器部署到每个 zone 并且它们是彼此的对等体。有关详细信息,请参阅区域和地区部分。

接下来,您需要告诉 Eureka 您的服务所在的区域。您可以使用metadataMap property 执行此操作。对于 example,如果将service 1部署到zone 1zone 2,则需要在service 1中设置以下 Eureka properties

Zone 1中的服务 1

eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true

Zone 2中的服务 1

eureka.instance.metadataMap.zone = zone2
eureka.client.preferSameZoneEureka = true