Spring Cloud Netflix 参考文档(1)——服务发现,Eureka客户端

英文原文链接——Service Discovery: Eureka Clients

介绍

2.2.5.RELEASE
本文档介绍了如何使用自动装配、绑定到 Spring 环境和其他 Spring 编程模型语法来将 Netflix OSS 组件集成到 Spring Boot 应用程序当中。通过一些简单的注解,您就可以使用经过大量实战检验过的 Netflix 组件快速地在您的应用程序中使用和配置一些通过模型以及构建大型地分布式系统。Netflix提供的模型包括服务发现(Eureka)、断路器(Hystrix)、智能路由(Zuul)和客户端负载均衡(Ribbon)。

1. 服务发现:Eureka 客户端

服务发现是基于微服务架构的关键原则之一。尝试手动配置每个客户端或某种形式的约定是很困难而且脆弱的。Eureka 是服务发现服务端,也是客户端。Eureka 可以配置和部署一个高可用的服务器,每个服务器都会将已注册的服务状态复制给其他服务器。

1.1. 如何引入 Eureka 客户端

为了在您的项目中使用 Eureka 客户端,需要在 pom 中引入以下依赖:

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

1.2 注册到 Eureka

当一个客户端注册到 Eureka 时,它提供关于自身的元数据——如主机、端口、运行状况指示器URL、主页和其他细节。Eureka 从属于某个服务的实例中获取心跳消息,如果在配置的时间内没有接受到心跳消息,该服务实例将从 Eureka 的注册表中移除。
以下代码展示了一个最简单的 Eureka 客户端程序。

@SpringBootApplication @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); } }

注意上面这个例子是一个简单的 Spring boot 应用。通过在 classpath 引入spring-cloud-starter-netflix-eureka-client,您的应用将自动注册到 Eureka 服务器。需要一些配置来定位 Eureka 服务器,如下所示:

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

在上面的例子中,defaultZone的值是一个神奇的字符串,它为那些没有特别设置的客户端提供了一个服务URL(简而言之,http://localhost:8761/eureka/是Eureka服务端的默认URL)

注意:由于serviceUrl属性是Map<String, String>, defaultZone 属性对大小写敏感并使用驼峰命名。因此,defaultZone属性并没有遵循Spring Boot 正常的 snake-case 的规范。

默认的应用程序名称 name(也就是service ID),虚拟的 host 和 port 的属性分别为${spring.application.name}, ${spring.application.name}${server.port}

在classpath中引入spring-cloud-starter-netflix-eureka-client使应用程序既是Eureka实例(它可以自己注册自己),又是一个客户端(它可以查询其他已注册的服务)。实例的行为由 eureka.instance.* 属性驱动,但是如果您在程序中配置了spring.application.name(这将作为默认的Service ID 或者说 VIP),那么eureka.instance.*使用默认值也是可以的。
查看 EurekaInstanceConfigBeanEurekaClientConfigBean 以获取可配置选项的更多细节。

您可以将 eureka.client.enabled 设置为 false 来禁用 Eureka 作为服务发现客户端。另外,将 spring.cloud.discovery.enabled 设置为 false 也将起到同样的效果。

1.3. 使用 Eureka 服务器进行身份验证

假如 eureka.client.serviceUrl.defaultZone 的 URL 中有一个包含了凭证(Credential, curl格式:user:password@localhost:8761/eureka),Http 基础身份验证功能将自动添加到您的 Eureka 客户端。为了满足更复杂的需求,您可以创建一个 DiscoveryClientOptionalArgs类型的@Bean,并将 ClientFilter 实例注入其中,所有这些都应用于客户机对服务器的调用。

注意:由于Eureka的限制,不可能为每台服务器的身份验证凭据都提供支持,所以只有第一个被发现的身份验证凭据才会生效。

当Eureka服务器需要客户端证书进行身份验证时,客户端证书 certificate 和 trust store 可以通过属性配置,如下面的示例所示:

eureka: client: tls: enabled: true key-store: <path-of-key-store> key-store-type: PKCS12 key-store-password: <key-store-password> key-password: <key-password> trust-store: <path-of-trust-store> trust-store-type: PKCS12 trust-store-password: <trust-store-password>

eureka.client.tls.enabled 需要设置为 true 来激活 Eureka 客户端的 TLS 功能。当 eureka.client.tls.trust-store 被省略时,JVM 默认的 trust store 将会生效。
eureka.client.tls.key-store-typeeureka.client.tls.trust-store-type 的默认值为PKCS12。当密码属性被省略时,假设密码为空。

1.4. 状态页和运行状况指示器

Eureka 实例的状态页和运行状况指示器分别为 /info/health。这是 Spring Actuator 应用程序中非常有用的端点的默认路径。
如果使用非默认上下文路径或servlet路径(如 server.servletPath=/custom ),那么即使对于Actuator应用程序,也需要更改这些内容。以下示例将展示这两个属性的默认设置:

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

这些链接显示在客户端使用的元数据中,并在某些场景中用于决定是否向应用程序发送请求,因此如果它们是准确的,这将很有帮助。

注意:在Dalston版本中,还需要在更改管理上下文路径时设置状态和运行状况检查url。这个要求从Edgware版本开始就被删除了。

1.5. 注册安全的应用程序

如果你的应用想使用 HTTPS 进行通讯,你可以设置 EurekaInstanceConfig 的两个标志:

  • eureka.instance.[nonSecurePortEnabled]=[false]
  • eureka.instance.[securePortEnabled]=[true]

这样做可以使 Eureka 发布显示对安全通信的显式偏好的实例信息。对于以这种方式配置的服务,Spring Cloud DiscoveryClient 总是返回一个以 https 开头的 URI 。类似地,当以这种方式配置服务时,Eureka(本机)实例信息有一个安全运行状况检查URL。

由于Eureka内部的工作方式,它仍然为状态和主页发布一个不安全的URL,除非您也显式地覆盖它们。您可以使用占位符来配置 eureka实例的 url,如下所示:

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

(注意,${eureka.hostname}是一个本地占位符,仅在 Eureka 的后续版本中可用。您也可以使用 Spring 占位符实现同样的功能——例如,使用${eureka.instance.hostName}。)

注意:假如您的应用程序使用了代理,并且SSL终端也被代理,(比如,您的应用运行在Cloud Foundry 或者其它平台作为一个服务),那么您需要确保代理的 header 属性 forwarded 被应用程序拦截和处理。 如果 Spring Boot 应用程序内置的 Tomcat 显式地配置了名为X-Forwarded-\*的 header 属性, 那么这将自动发生。如果您的应用程序的链接呈现其是错误的(错误的主机、端口或协议),这是一个信号,表明您的配置是错误的。

1.6 Eureka 的健康检测

默认情况下,Eureka 使用客户端心跳机制来确定一个客户端是否启动。除非有特定说明,否则 Discovery Client 不会通过 Spring Boot Actuator 传播应用程序的当前运行状况检查状态。因此,在成功注册后,Eureka总是报告应用程序处于“启动”状态。可以通过启用Eureka运行状况检查来更改此行为,这将导致将应用程序状态传播到 Eureka。因此,任何一个应用程序都不会向别的程序发送除了“启动”状态意外的其他信息。以下示例将展示如何在一个客户端中启用健康检测:

eureka: client: healthcheck: enabled: true

警告: 只能在 application.yml 文件中设置eureka.client.healthcheck.enabled=true。在 bootstrap.yml 中设置该属性将导致意外错误,比如向 Eureka 注册一个状态为 UNKNOWN 的服务。

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

1.7 Eureka 实例和客户端的元数据

花费一些时间来理解 Eureka 元数据的工作原理是值得的,这样您就可以在您的平台上用合理的方式使用它。对于诸如主机名(hostname)、IP地址(IP)、端口号(port)、状态页(status page)和健康检测(health check)等信息,有标准的元数据。它们在服务注册中心中发布,客户机使用它们以一种直接的方式联系服务。可以在 服务配置中心的eureka.instance.metadataMap中添加其他元数据。这些元数据可以在远程客户端访问。通常,额外添加的元数据不会改变客户端的行为,除非客户端知道这些元数据的意义。之后的文档中我们将描述一些特殊的情况,这些情况下,Spring Cloud 已经为元数据映射指定了特殊的意义》

1.7.1 在 Cloud Foundry 中使用 Eureka

Cloud Foundry 有一个全局的路由器,所以一个应用程序的所有实例有一个相同的 hostname(其他类似体系结构的PaaS解决方案也有一样的特点)。这并不一定是使用 Eureka 的障碍。
但是,如果您使用路由器(推荐的,甚至是强制的,这取决于平台的设置方式),您需要显式地设置主机名 hostname 和端口号 port (安全的或不安全的),以便它们使用路由器。您可能还希望使用实例的元数据,以便能够区分客户机上的实例(例如,在自定义负载均衡器中)。默认情况下,eureka.instance.instanceId 就是 vcap.application.instance_id,如下所示:

application.yml

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

根据在您的Cloud Foundry实例中设置安全规则的方式,您也许能够注册和使用主机VM的IP地址来进行直接的service-to-service之间的调用。这个特性在Pivotal Web Services(PWS)还是不可用的。

1.7.2. 在 AWS 上使用 Eureka

如果计划在 AWS 云上部署应用,Eureka 实例必须设置为 AWS-aware。您可以像下面这样自定义 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; }

1.7.3 更改 Eureka 实例的 ID

一个普通的 Netflix Eureka 实例使用一个ID注册,这个ID等于它的主机名(hostname)(也就是说,每个主机只有一个服务)。Spring Cloud Eureka提供了一个合理的默认值,其定义如下:
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}
一个例子是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)上的多个服务实例,将在其中插入随机值以使该实例唯一。在 Cloud Foundry 中,vcap.application.instance_id 会自动填充到Spring引导应用程序中,因此不需要随机值。

1.8 使用 EurekaClient

一旦你有了一个作为 Discovery Client 的应用程序,你可以使用它从 Eureka Server 中发现服务实例。这样做的一种方式是使用原生的com.netflix.discovery.EurekaClient(而不是Spring Cloud DiscoveryClient),如下所示:

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

注意:不要在 @PostConstruct 方法或 @Scheduled 方法(或任何 ApplicationContext 还没有启动的地方)中使用 EurekaClient。它在一个SmartLifecycle (phase=0)中初始化,所以您可以确保它可用的最早时间点是在另一个phase更大的 SmartLifecycle 中。

1.8.1 从 EurekaClient 中移除 Jersey

EurekaClient 默认使用 Jersey 作为 HTTP 通讯客户端。 如果您不想依赖 Jersey,您可以在您的依赖中移除它。Spring Cloud 自动配置了一个基于 Spring RestTemplate 的传输客户端。以下代码将展示如何移除 Jersey:

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</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>

1.9. Netflix 原生 EurekaClient 的替代品

你不需要使用原始的 Netflix EurekaClient。而且,在某种包装器下使用它通常更方便。Spring Cloud 通过使用逻辑Eureka服务标识符而不是物理 URL 来支持 FeighSpring RestTemplate。您可以设置以逗号’,'分隔物理地址(或者hostname)的列表<client>.ribbon.listOfServers来使用一个固定的物理服务器列表配置 Ribbon,这里,<client> 是客户端的 ID。

您也可以使用org.springframework.cloud.client.discovery.DiscoveryClient, 它提供了简单的API(不止用于 Netflix )来发现服务,如下所示:

@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; }

1.10. 为什么注册服务这么慢?

注册到服务注册中心意味着需要一个周期为30秒的心跳消息(通过serviceUrl)。直到服务实例,注册中心和客户端在它们的内存中有着相同的元数据,否则客户端是不会发现这个服务的(这意味着注册过程需要3个心跳消息)。你可以通过修改 eureka.instance.leaseRenewalIntervalInSeconds 属性来修改心跳周期。将该值设置为小于 30s 可以提升客户端连接到其他服务的速度。在生产环境中,由于服务端的内部计算对租约续订期进行了假设,所以最好使用默认值。

1.11. Zones

如果您已经将同一个客户端部署在多个区域(Zone),您可能希望客户端在尝试另一个区域的服务之前先使用同一个区域的服务。为了达到这个目的,您需要正确配置您的 Eureka 客户端。

首先,您需要确保每个区域都部署了Eureka服务器,并且它们彼此是对等的。有关更多信息,请参阅区域和区域一节

然后,您要告诉 Eureka 您的服务在哪个区域(Zone),您可以使用metadataMap来实现它。举个例子:如果 service 1zone 1zone 2 都有部署,您需要给 service 1 设置以下 Eureka 属性:
zone 1 中的 service 1

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

zone 2 中的 service 1

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

1.12. 刷新 Eureka 客户端

默认情况下,EurekaClient bean是可刷新的,这意味着Eureka客户端属性可以更改和刷新。当刷新发生时,将取消从Eureka服务器注册的客户端,可能会有一段短暂的时间,即给定服务的所有实例都不可用。消除这种情况的一种方法是禁用刷新 Eureka 客户端的功能。为此设置eureka.client.refresh.enable=false

1.13. 使用Eureka和Spring Cloud负载均衡器

我们提供了对Spring Cloud LoadBalancer的支持ZonePreferenceServiceInstanceListSupplier,Eureka 实例中元数据的 zone 值(eureka.instance.metadataMap.zone)将被用于设置 spring-clod-loadbalancer-zone,这个值用于按区域筛选服务实例。

如果该值缺省并且spring.cloud.loadbalancer.eureka.approximateZoneFromHostname 被设置为 false,它可以使用来自服务器主机名的域名作为区域的代理。

如果没有区域数据的其他来源,则会根据客户端配置(与实例配置相反)进行猜测。我们使用 eureka.client.availabilityZones(从区域名称到区域列表的映射)的第一个 zone 作为 Eureka 实例的地区(也就是eureka.client.region,默认值是 us-east-1,这与原生的 Netflix 是兼容的)。

阅读(262)
评论(0)
updated@2020-12-08
评论区
目录