《Spring Cloud Netflix官方文档》1.服务发现:Eureka客户端

原文链接 译者:Acamy

1.     服务发现:Eureka客户端

服务发现是微服务架构的关键原则之一。使用手动配置或一些约定方式来处理多服务多实例的方式是非常困难,并且十分脆弱的。Eureka同时是Netflix服务发现的服务端和客户端。服务端可以通过配置和部署实现高可用,实现方式是每个服务端对注册的服务复制他们的状态到其他的服务端。

1.1  如何创建Eureka客户端

引入org.springframework.cloud的spring-cloud-starter-eureka工程就可以在你的项目上中创建Eureka客户端。在当前版本上如何具体配置可以查看Spring Cloud 工程页面

1.2  使用Eureka注册

当一个客户端用Eureka注册时,它会提供诸于主机和端口,健康指标URL,主页等元数据。Eureka接收来一个服务的所有实例的心跳信息。如果有在配置时间段内没有接收到心跳,这个实例通常会从注册表中移除。
Eureka客户端的例子如下:

[code lang=”java”]
@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableEurekaClient
@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);
}

}
[/code]

(即非常普通的Spring Boot应用)。在这个例子中我们使用了EnableEurekaClient注解,但是当只有Eureka可用时你也可以使用EnableDiscoveryClient注解。还需要如下所示的配置信息来定位Eureka服务端:

application.yml

[code lang=”java”]
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
[/code]

其中的defaultZone为未表示首选项的客户端提供默认的服务,是一个有后备值的魔力字符串 (即它是一个有用的默认参数)。

该环境下的默认应用名(服务ID),虚拟主机名和非安全端口号分别是 ${spring.application.name}, ${spring.application.name} 和 ${server.port}。

@EnableEurekaClient使得该应用成为一个Eureka “实例”(即自注册)和一个“客户端”(即它能查询注册表以定位其它服务)。eureka.insatnce.*配置信息决定了实例的行为,但是如果你确保你的应用有spring.application.name(这就是Eureka服务ID的默认值,或者说是VIP),那么默认配置也是可以的。

更多的配置选项可以查看 EurekaInstanceConfigBean 和 EurekaClientConfigBean

1.3  Eureka服务器认证

如果一个eureka.client.serviceUrl.defaultZone的URL内嵌了认证(如:http://user:password@localhost:8761/eureka),那么基于HTTP的认证机制就会自动加入到你的eureka客户端中。如果有更复杂的需求可以创建一个DiscoveryClientOptionArgs类型的Bean并注入ClientFilter实例,当客户端请求服务端时这些配置就会生效。

注意因为Eureka的限制,不可能去支持每一个服务器认证证书,而是使用第一个被发现的。

1.4  状态页和健康检查

Eureka实例的状态和健康检查页面默认分别是”/info”和“/health”,它们是Spring Actuator应用中有用的默认定位设置。如果你使用一个非默认的上下文路径或者 servlet路径(例如server.serveletPath=/foo)或者管理端路径(例如management.contextPath=/admin),那么你就需要改变这些,即使Actuator应用。示例如下:

application.yml.

[code lang=”java”]
eureka:
instance:
statusPageUrlPath: ${management.context-path}/info
healthCheckUrlPath: ${management.context-path}/health
[/code]

这些链接是客户端之间互相使用的元数据,并且在一些情形下用来决定是否向应用发送请求,所以如果它们是精准的话将会是有帮助的。

1.5  注册安全应用

如果你希望应用使用HTTPS协议进行交互,那么你可以在EurekaInstanceConfig中配置两个指标,即eureka.instance.[nonSecurePortEnabled,securePortEnabled]=[false,true].这将使得Eureka发布的实例信息首选安全通信。Spring Cloud的发现客户端就总是会为采取该种配置的服务返回一个以https开头的URI,并且Eureka(本地)实例信息会有一个安全的健康检查URL。

因为Eureka的内部工作机制,它仍然会给状态和主页产生一个非安全的URL,除非你显示重写配置。你可使用占位符来配置eureka实例的urls,例如:

application.yml

[code lang=”java”]
eureka:
instance:
statusPageUrl: https://${eureka.hostname}/info
healthCheckUrl: https://${eureka.hostname}/health
homePageUrl: https://${eureka.hostname}/
[/code]

(注意${eureka.hostname}是一个只能在Eureka后续版本中使用的本地占位符,你可以使用Spring占位符来达到同样的效果,例如使用${eureka.instance.hostname})

如果你的app在代理下运行,并且SSL终端也使用了代理(例如如果你在Cloud Foundry或者其它平台下作为一个服务运行),那么你就需要确保代理的”forwarded”头被拦截并且被该应用处理。如果内嵌Tomcat容器的Spring Boot应用对‘X-Forwarded-\*’头有一个明确的配置,那么它就会自动处理这些。出现该类错误的一个标志就是你的app自身的一些链接会出现错误(错误的主机,端口和协议)。

1.6  Eureka的健康检查

Eureka默认会根据客户端的心跳来判断该客户端是否在线。除非是具体指定了否则的话发现客户端是不会将通过Spring boot Actuator传递当前应用的健康检查状态。也就是意味着一旦成功注册Eureka之后就会一直处于在线状态。当启用Eureka健康检查之后就会改变这种状态,致使应用的状态传递给Eureka。结果就是其它的每一个应用除非是处于在线状态,否则不会向应用发送请求。

application.yml

[code lang=”java”]
eureka:
client:
healthcheck:
enabled: true
[/code]

警告eureka.client.healthcheck.enabled=true只能在application.yml中配置。如果在bootstrap.yml中配置的话会产生不良影响,比如说在eureka中注册为UNKNOWN状态。
如果需要对健康检查作更多控制,你可以考虑实现类com.netflix.appinfo.HealthCheckHandler。

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

为了在平台中合理使用Eureka,花点时间理解Eureka元数据是如何工作是非常值得的。标准的元数据就是诸如主机名,IP地址,端口号,状态页和健康检查。这些都包含在服务注册表中,并且被客户端用来直接与服务联系。可以在服务注册时加入其它元数据到eureka.instance.metadataMap中,远程客户端也能使用这些数据,除非改变元数据产意义,否则总体上不会改变客户端的行为。如下几种特殊情形Spring Cloud已经指定了元数据集的意义。

1.7.1    在Cloudfoundry上用Eureka

Cloudfoundry有一个全局路由器,因此同一个应用的所有实例有相同的主机名(其它相似架构的Paas解决方案中也是如此)。这并不是使用Eureka的障碍,但是如果你使用路由器(推荐,甚至是强制性的,具体取决于平台的设置方式),你需要设置具体的主机名和端口号(安全或非安全的),以便他们使用路由器。你可能想通过实例元数据来区分客户端的实例(比如说一个传统的负载均衡器中)。默认情况下,eureka.instance.instanceId就是vcap.application.instance_id。例如:

application.yml

[code lang=”java”]
eureka:
instance:
hostname: ${vcap.application.uris[0]}
nonSecurePort: 80
[/code]

通过在Cloudfoundry实例中设置的安全规则,你就可以将虚拟主机注册成服务对服务的调用并且可以使用。该功能在Pivotal Web Services(PWS)上还不可用。

1.7.2    在AWS上用 Eureka

如果应用要部署在AWS云,那么Eureka实例就必须能让AWS识别,可以通过如下定制EurekaInstanceConfigBean来做到:

[code lang=”java”]
@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;
}
[/code]

1.7.3    修改Eureka实例ID

一个比较好的Netflix Eureka实例的注册ID就是主机名(即一个主机只有一个服务),Spring Cloud Eureka提供了诸如${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}的合理默认形式。例如myhost:myappname:8080。

你可以通过在eureka.instance.instanceId中提供一个唯一的标识来覆盖这些,例如:

application.yml

[code lang=”java”]
eureka:
instance:
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
[/code]

通过这个元数据,当在本地部署多服务实例后,就会有随机值来确保实例是唯一的。在CloudFoundry中vcap.application.instance_id在Spring Boot应用中自动填充,因此不需要随机值。

1.8  使用Eureka客户端

一旦你的应用采用了@EnableDiscoveryClient(或者@EnableEurekaClient),你就可以发现Eureka服务器上的服务实例。其中的一种方式就是使用类com.netflix.discovery.EurekaClient(而不是Spring Cloud DiscoveryClient),例如:

[code lang=”java”]
@Autowired
private EurekaClient discoveryClient;

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

提示:不要在@PostConstruct、@Scheduled方法(或者是任何上下文还没有启动的位置)使用EurekaClient。它是在智能生命周期(阶段为0)时被初始化的,所以你最早能够使用它是在另一个智能生命周期的更高阶段。

1.9  对原始Netfix Eureka客户端所做的更改

你并不非得全用原生的Netflix EurekaClient,通常情况下经过某种方式包装后使用起来将更方便。Spring Cloud已经支持Feign(一个REST客户端builder)和使用逻辑Eureka服务定位器(VIPs)来替代物理URL的Spring RestTemplate。将Ribbon配置成固定的物理服务器列表只需简单将<client>.ribbon.listOfServers配置成用逗号分隔物理地址列表(或主机名),其中的<client>表示客户端的ID。

你也可以使用并非为Netflix定制的org.springframework.cloud.client.discovery.DiscoveryClient,该类为发现客户端提供了一个简单的API,例如:

[code lang=”java”]
@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;
}
[/code]

1.10      为什么注册一个服务这么慢?

实例需要间隔性地向注册表发送心跳(通过客户端的serviceUrl),默认间隔是30秒。一个服务只有当实例,服务器和客户端在他们的本地缓存中都有相同的元数据后才能被客户端发现(因此需要三个心跳)。可以通过eureka.instance.leaseRenewalIntervalInSeconds来更改心跳周期,它能加快客户端到服务端的速度。在生产环境中最好是使用默认值,因为服务器内部有一些对更新周期进行的计算。

1.11      区块

如果将Eureka客户端部署在多个区块上,并且在使用时优先使用一个区,那么就需要正确地配置Eureka客户端。

首先需要确保Eureka服务器部署到了每一个区块并且是彼此的节点。可以在zones and regions部分查看更多信息。

其次你需要告诉Eureka你的服务在哪一个区块,可以通过metadataMap属性来进行设置。例如如果服务1部署在区块1和区块2上,你需要在服务1中进行如下Eureka属性的设置

区块1中的服务1

[code lang=”java”]
eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true
[/code]

区块2中的服务1

[code lang=”java”]
eureka.instance.metadataMap.zone = zone2
eureka.client.preferSameZoneEureka = true
[/code]

  • Trackback 关闭
  • 评论 (0)
  1. 暂无评论

return top