(二)服务治理:Spring Cloud Eureka --笔记


Spring Cloud Eureka是Spring Cloud Neflix微服务套件中的一部分,它基于Netflix Eureka做了二次封装,主要负责完成微服务架构中的服务治理功能。

服务治理

服务治理是微服务架构中最为核心和基础的模块,它主要用来实现各个微服务实例的自动化注册与发现。

  • 服务注册:在服务助理框架中,通常都会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务,将主机与端口号、版本号、通信协议等一些附加信息告知注册中心,注册中心按服务名分类组织服务清单。
  • 服务发现:由于在服务治理框架下运作,服务间的调用不再通过指定具体的实例地址来实现,而是通过向服务名发起请求调用实现。实际的框架为了性能,不会采用每次都向服务注册中心获取服务的方式,并且不同的应用场景在缓存和服务剔除等机制上也会有一些不同的实现策略。

Netflix Eureka

Spring Cloud Eureka,使用Netflix Eureka来实现服务注册与发现,它既包含了服务端组件,也包含了客户端组件,并且均采用Java编写,所以Eureka主要适用于通过Java实现的分布式系统,或是与JVM兼容语言构建的系统。当然它也支持非Java语言的微服务应用。

Eureka服务端,我们也称为服务注册中心。它同其他服务注册中心一样,支持高可用配置。Eureka以集群部署时,当集群中有分片出现故障时,那么Eureka就转入自我保护模式,它允许在分片故障期间继续提供服务的发现和注册,当故障分片恢复运行时,集群中的其他分片会把他们的状态再次同步回来。

Eureka客户端,主要处理服务的注册与发现。客户端服务通过注解和参数配置的方式,嵌入在客户端应用程序的代码中。在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并周期性地发送心跳来更新它的服务租约。同时,它也能从服务端查询当前注册的服务信息并把它们缓存到本地并周期性地刷新服务状态。

搭建服务注册中心

通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。

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

默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需要在application.properties中添加:

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
  • eureka.client.register-with-eureka:由于该应用为注册中心,所以设置为false,代表不向注册中心注册自己。
  • eureka.client.fetch-registry:由于注册中心的职责就是维护服务实例,它并不需要去检索服务,所以也设置为false。

注册服务提供者

在主类中通过添加@EnableDiscoveryClient注解,激活Eureka中的DiscoverryClient实现。

同时需要在application.properties文件中,通过spring.application.name属性来为服务命名。再通过eureka.client.serviceUrl.defaultZone属性来指定服务注册中心的地址:

spring.application.name=hello-service
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

高可用注册中心

Eureka Server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,以实现服务清单的互相同步,达到高可用的效果。

构建双节点的服务注册中心集群:

  • 创建application-peer1.properties,作为peer1服务中心的配置,并将serviceUrl指向peer2:
spring.application.name=eureka-server
server.port=1111

eureka.instance.hostname=peer1
eureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka/
  • 创建application-peer2.properties,作为peer2服务中心的配置,并将serviceUrl指向peer1:
spring.application.name=eureka-server
server.port=1112

eureka.instance.hostname=peer2
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/
  • 在/etc/hosts/文件中添加对peer1和peer2的转换,让上面配置的host形式的serviecUrl能在本地正确访问到:Windows系统路径为C:\Windows\System32\derivers/etc/hosts。
127.0.0.1 peer1
127.0.0.1 peer2
  • 通过spring.profiles.active属性来分别启动peer1和pee2:
java -jar eureka-server-1.0.0jar --spring.profiles.active=peer1
java -jar eureka-server-1.0.0jar --spring.profiles.active=peer2
  • 在设置了多节点的服务注册中心之后,服务提供方还需要做一些简单的配置才能将服务注册到Eureka Server集群中。以hello-service为例,修改application.properties配置文件,如下:
spring.application.name=hello-service
eureka.client.serviceUrl.ddefaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/

如果我们不想使用主机名来定义注册中心的地址,也可以使用IP地址的形式,但需要在配置文件中增加配置参数,默认为false:
server端:

eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
eureka.instance.prefer-ip-address=true
eureka.instance.hostname= ${spring.cloud.client.ipAddress}
spring could 2.0版本 需要改成${spring.cloud.client.ip_address}

服务发现与消费

已经有了Eureka服务注册中心和hell-servive的服务提供者,下面构建一个服务消费者,它主要完成发现服务以及消费服务。其中,服务发现的任务由Eureka的客户端完成,而服务消费的任务由Ribbon完成。Ribbon是一个基于HTTP和TCP的客户端负载均衡器,它可以在通过客户端中配置的ribbonServerList服务端列表取轮询访问以达到均衡负载的作用。当Ribbon与Eureka联合使用时,Ribbon的服务实例清单RibbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心获取服务端列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。Ribbon在Eureka服务发现的基础上,实现了一套对服务实例的选择策略,从而实现对服务的消费。

  • 首先,启动之前的的eureka-server和hello-service服务,hello-service通过–server.port启动2个:
java -jar hello-service-0.0.1.jar --server.port=8081 
java -jar hello-service-0.0.1.jar --server.port=8082
  • 创建一个Spring Boot基础工程,取名ribbion-consumer,并引入依赖spring-cloud-starter-ribbon。
  • 创建应用主类ConsumerApplication,通过@EnableDiscoverClient注解,以获得服务发现的能力。同时创建RestTemplate的Spring Bean实例,并通过@LoadBalnaced注解开启客户端负载均衡。
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication{
	
	@Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new Resttemplate();
	}

	public static void main(String[] args){
		SpringApplication.run(ConsumerApplication.class,args);
	}
}
  • 创建ConsumerController类并实现/ribbon-consumer接口。在接口中,通过在上面创建的RestTemplate来实现对HELLO-SERVICE服务提供的/hello接口进行调用。可以看到这里访问的地址是服务名HELLO-SERVICE,而不是一个具体的地址,在服务治理框架中,这是一个非常重要的特性。
@RestController
public class ConsumerController{

	@Autowired
	RestTemplate restTemplate;
	
	@RequestMapping(value="/ribbon-consumer",method=RequestMethod.GET)
	public String helloConsumer(){
		return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
	} 

}
  • 在application.properties中配置的Eureka服务注册中心的位置,需要与之前的HELLO-SERVICE一样:
spring.application.name=ribbon-consumer
server.port=9000

eureka.client.serviceUrl.defaultZone=htto://localhost:1111/eureka/

Eureka 详解

基础结构

整个Eureka服务治理基础架构的三个核心要素:
服务注册中心。服务提供者、服务消费者。

服务治理机制

下图中有几个重要元素:

  • “服务注册中心-1”和“服务注册中心-2”,它们互相注册组成了高可用集群。
  • “服务提供者”启动了两个实例,一个注册到“服务注册中心-1”上,另一个注册到“服务注册中心-2”上。
  • 还有两个“服务消费者”,它们也都分别值指向了一个注册中心。

这里写图片描述

下面了解下,从服务注册开始到服务调用,及各个元素所涉及的一些重要通信行为。

服务提供者

服务注册

“服务提供者”在启动时或通过发送REST请求的方式将自己注册到Eureka Server上,同时带上自身服务的一些元数据。Eureka Server接收到这个REST请求后,将元数据信息存储在一个双层结构Map中,其中第一层的key是服务名,第二层的key是具体服务的实例名。(回想下之前在实现Ribbon负载均衡的例子中,Eureka信息面板中一个服务有多个实例的情况,这些内容就是以这样的双层Map形式存储的)

在服务注册时,需要确认下eureka.client.register-with-eureka=true参数是否正确,默认为true,若设置为false将不会启动注册操作。

服务同步

这里的两个服务提供者分别注册到了两个不同的服务注册中心之上,它们的信息分别被两个服务注册中心所维护。此时,由于服务注册中心之间因互相注册为服务,当服务提供者发送注册请求到一个服务注册中心,它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步。通过服务同步,两个服务提供者的服务信息就可以通过这两台服务注册中心中的任意一台获取到。

服务续约

在注册完服务之后,服务提供者会维护一个心跳用来持续高速Eureka Server:“我还活着”,以防止Eureka Server的“剔除任务”将该服务实例从服务列表中排除出去,称该操作为服务续约(Renew)。

关于服务续约有两个重要属性,可以关注并根据需要来进行调整:

eureka.instance.lease-renewal-interval-in-seconds=30
eureka.instance.lease-expiration-duration-in-seconds=90

eureka.instance.lease-renewal-interval-in-seconds参数用于定义服务续约任务的调用间隔时间默认30秒。eureka.instance.lease-exptration-duration-in-seconds参数用于定义服务失效时间,默认为90秒。

服务消费者

获取服务

当我们启动服务消费者时候,它会发送一个REST请求给服务注册中心,来获取上面注册的服务清单。为了性能考虑,Eureka Server会维护一份只读的服务清单来返回给客户端,同时该缓存青岛会每隔30秒更新一次。

获取服务是服务消费者的基础,必须确保eureka.client.fetch-registry=true参数没有被修改成为false,该值默认为true。若希望修改缓存清单的更新时间,可以通过eureka.client.registry-fetch-interval-seconds=30参数进行修改,默认30,单位为秒。

服务调用

服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据。因为有这些服务实例的详细信息,所以客户端可以根据自己的需要决定具体调用哪个实例,在Ribbon中会默认采用轮询的方式进行调用,从而实现客户端的负载均衡。

对于访问实例的选择,Eureka中有Rigion和Zone的概念,一个Region中可以包含多个Zone,每个服务客户端需要被注册到一个Zone中,所以每个客户端对应一个Region和一个Zone。在进行服务调用时候,优先访问同处一个Zone中的服务提供方,若访问不到,就访问其他的Zone。

服务下线

当服务实例进行正常的关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我下线了”。注册中心接收到请求之后,将该服务状态置为下线(DOWN),并把该下线事件传播出去。

服务注册中心

失效剔除

我们的服务实例并不一定会正常下线,为了从服务列表中将这些无法提供服务的实例剔除,Eureka Server在启动的时候会创建一个定时任务,默认每隔一段时间(默认60秒)将当前清单中超时(默认为90秒)没有续约的服务剔除出去。

自我保护

默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。

服务注册到Eureka Server之后,会维护一个心跳连接,高速Eureka Server自己还活着。Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况,Eureka Server会将当前的实例注册信息保护起来,让这些实例不会过期,尽可能保护这些注册信息。

eureka.instance.leaseRenewalIntervalInSeconds = 30 # hearbeat interval
eureka.server.renewalPercentThreshold = 0.85
eureka.server.renewalThresholdUpdateIntervalMs = 15 * 60 * 1000 # 15mins 

leaseRenewalIntervalInSeconds:client发送心跳的频率
renewalPercentThreshold:触发自我保护的心跳数比例阈值
renewalThresholdUpdateIntervalMs:多久重置一下心跳阈值

当Eureka Server自动进入自我保护机制,会出现以下几种情况:

1、Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
2、Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
3、当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

在这段保护期间内实例若出现问题,那么客户端很容易按到实际不存在的服务实例,会出现调用失败的情况,所以客户端必须要有容错机制,比如可以使用请求重试、断路器等机制。

自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。

由于本地调试很容易触发注册中心的保护机制,这会使得注册中心维护的服务实例不那么准确。所以,本地开发时,可以使用eureka.server.enable-self-preservation=false参数来关闭保护机制,以确保注册中心可以将不可用实例正确剔除。

服务消费者默认30s修改缓存清单。
服务提供者心跳续约默认30s一次,如果90s内没有心跳服务Eureka Server将服务置为超时,Eureka维护一个线程,默认60s将超时没有续约的服务剔除。或者服务提供者主动下线通知Eureka Server将服务剔除。
Eureka Server的保护机制:当心跳失败比例在15分钟内<85%,Eureka Server会将当前实例注册信息保存,让这些实例不会过期。所以当实例被保护起来,90秒超时就不起作用,所以这是应该监控到自我保护机制的开启,人工查看服务的可用性,或者在程序中使用日志及消息提醒。当然可以主动关闭保护机制。

源码分析

对于服务注册中心、服务提供者、服务消费者这三个主要元素来说,后两者(也就是Eunreka客户端)在真个运行机制中是大部分通信行为的主动发起者,而注册中心是主要是处理请求的接收者。我们从Eureka的客户端作为入口看看他如何完成这些主动通信行为。

一个普通的Spring Boot应用注册到Eureka Server或是从Eureka Server中获取服务列表时,主要做了两件事:

  • 在应用主类中配置了@EnableDiscoveryClient注解。
  • 在application.properties中用eureka.client.serviceUrl.defaultZone参数指定了服务注册中心的位置。

看下@EnableDiscoveryClient的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Document
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient{
}

从该注解的注释中我们可以知道,它主要用来开启DiscoveryClient的实例。通过搜索DiscoveryClient,我们可以发现有一个类和一个接口。

这里写图片描述

其中,左边的org.springframework.cloud.client.discovery.DiscoveryClient是Spring Cloud的接口,它定义了用来发现服务的常用抽象方法,通过该接口可以有效地屏蔽服务治理的实现细节,所以使用Spring Cloud构建的微服务应用可以方便地切换不同服务治理框架,而不用改动程序代码,只需要另外添加一些针对服务治理框架的配置即可。org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient是对该接口的实现,从命名看,它实现的是对Eureka发现服务的封装。所以EurekaDiscoveryClient依赖了Netflix Eureka的com.netflix.discovery.EurekaClient接口,EurekaClient继承了LookupService接口,它们都是Netflix开源包中的内容,主要定义了针对Eureka的发现服务的抽象方法,而真正实现发现服务的则是Netflix包中的com.netflix.discovery.DiscoveryClient类。

解读下DiscoveryClient类头部的注释:

这个类用于帮助与Eureka Server互相协作。

Eureka Client负责线面的任务:
- 向Eureka Server注册服务实例
- 向Eureka Server服务子曰
- 当服务关闭期间,向Eureka Server取消租约
- 查询Eureka Server中的服务实例列表

Eureka Client还需要配置一个Eureka Server的URL列表。

先看下哪里多Eureka Server的URL列表进行配置。根据我们配置的属性名eureka.client.serviceUrl.defaultZone,通过serviceUrl可以找到该属性相关的加载属性,但是在SR5版本中它们都被@Deprecated标注为不在建议使用,并@link到了替代类com.netflix.discovery.endpoint.EndpointUtils。

Region、Zone

客户端依次加载了两个内容,第一个是Region,第二个是Zone,从加载逻辑上我们可以判断出它们之间的关系:

  • 通过getRegion函数,可以看到它从配置中读取了一个Region返回,所以一个微服务应用只可以属于一个Region,如果不特别配置,默认为default。若自己设置,可以通过eureka.client.region属性来定义。
    • 通过getAvailabilityZones函数,可以知道当我们没有特别为Region配置Zone时,将默认采用defaultZone,这也就是配置参数eureka.client.serviceUrl.defaultZone的由来。若要为应用指定Zone,可以通过eureka.client.availability-zones属性来设置,Zone能够设置多个,并且通过逗号分隔来配置。Region与Zone是一对多的关系。

serviceUrls

在获取了Region和Zone的信息之后,才开始真正加载Eureka Server的具体地址。它根据传入的参数按一定算法确定加载位于哪一个Zone配置的serviceUrls。

具体获取servcieUrls的实现,我们可以详细查看getEurekaServerServiceUrls函数的具体实现类EurekaClientConfigBean,该类时EurekaClientConfig和EurekaConstants接口的实现,用来加载配置文件中的内容。我们先说下defaultZonde的信息,通过搜索defaultZone,可以很容易找到public List getEurekaServerServiceUrls(String myZone)这个函数,它具体实现了如何解析该参数的过程,通过此内容可以知道eureka.client.serviceUrl.defaultZone属性可以配置多个,并且需要通过逗号分隔。

当我们在微服务应用中使用Ribbon来实现服务调用时,对于Zone的设置可以在负载均衡时实现区域亲和特性:Ribbon的默认策略会优先访问同客户端处于一个Zone中的服务端实例,只有当同一个Zone中没有可用服务端实例的时候才会访问其他Zone中的实例。所以通过Zone属性的定义,配合实际部署的物理结构,就可以有效地设计出区域性故障容错集群。

服务注册

在理解了多个服务注册中心信息的加载后,我们在回头看看DiscoveryClient类是如果实现“服务注册”行为的,通过查看它的构造类,可以看到一个与服务注册相关的判断语句if(clientConfig.shouldRegisterWithEureka())。在该分支内,创建了一个InstanceInfoReplicator类的实例,它会执行一个定时任务,而这个定时任务的执行discoveryClient.register();这一行,真正触发调用注册的地方就在这里。注册操作也是通过REST请求的方式进行的。同时,我们可以看到发起注册请求的时候,传入了一个com.netflix.appinfo.InstanceInfo对象,该对象就是注册时客户端给服务端的服务的元数据。

服务获取与服务续约

DuscoveryClient的initScheduledTasks函数中还有两个定时任务,分别是“服务获取”和“服务续约”。

“服务获取”任务相对于“服务续约”和“服务注册”任务更为独立。“服务续约”与“服务注册”在同一个if逻辑中,服务注册到Eureka Server后,自然需要一个心跳去续约,防止被剔除,所以“服务续约”和“服务注册”肯定是成对出现的。

而“服务获取”的逻辑在独立的一个if判断中,其判断依据就是之前提到的eureka.client.fetch-registry=true参数,默认为true,大部分情况我们无需关心。为了定期更新客户端的服务清单,以保证客户端能够访问确实健康的服务实例,“服务获取”的请求不会只限于服务启动,而是一个定时执行的任务。

“服务续约”实现较为简单,直接以REST请求的方式进行续约。

而“服务获取”则复杂一些,会根据是否是第一次获取发起不同的REST请求和相应处理。

服务注册中心处理

所有的交互都是通过REST请求来发起的。Eureka Server对于各类REST请求的定义都位于com.netflix.eureka.resources包下。

以“服务注册”请求为例,再对注册信息进行了一堆检验之后,会调用org.springframework.cloud.netflix.eureka.server.InstanceRegistry对象中的register(InstanceInfo info,int leaseDuration,boolean isReplication) 函数来进行服务注册。

在注册函数中,先调用publishEvent函数,将该新服务注册的事件传播出去,然后调用com.netflix.eureka.registry.AbstractInstanceRegistry父类中的注册实现,将InstanceInfo中的元数据信息存储在一个ConcurrentHashMap对象中。

正如之前所说,注册中心存储了两层Map结构,第一层的key存储服务名:InstanceInfo中的appName属性,第二层key存储实例名:InstranceInfo中的instranceId属性。

配置详解

在Eureka的服务治理体系中,主要分为服务端与客户端两个不同的角色,服务端为服务注册中心,而客户端为各个提供接口的微服务应用。当我们构建了高可用的注册中心之后,该集群中所有的微服务应用和后续的一些基础类应用(如配置中心、API网关等)都可以视作该体系下的一个微服务(Eureka客户端)。服务注册中心也一样,只是高可用环境下的服务注册中心除了作为客户端之外,还为集群中的其他客户端提供了服务注册的特殊功能。所以,Eureka客户端的配置对象存在于所有Eureka服务治理体系下的应用实例中。在实际使用Spring Cloud Eureka的过程中,我们所做的配置内容几乎都是对Eureka客户端配置进行的操作。

Eureka客户端的配置主要分为以下两个方面:

  • 服务注册相关的配置信息,包括服务注册中心的地址、服务获取的间隔时间、可用区域等。
  • 服务实例相关的配置信息,包括服务实例的名称、IP地址、端口号、健康检查路径等。

Eureka服务端更多类似一个现成产品,大多数情况下,不需要修改它的配置信息。

服务注册类配置

关于服务注册类的配置信息,我们可以通过查看org.springfremawork.cloud.netflix.eureka.EurekaClientConfigBean的源码来获取更多内容,这些配置信息都以eureka.client为前缀。

指定注册中心

一般Spring Boot纳入Eureka服务治理体系,除了引入Eureka的依赖之外,就是在配置中心中指定注册中心,主要通过eureka.client.serviceUrl参数实现。它的配置值存储在HashMap类型中,并且设置有一组默认值,默认值的key位defaultZone、value为:http://localhost:8761/eureka/。

private Map<String,String> serviceUrl = new HashMap<>();
{
	this.serviceUrl.put(DEFALUT_ZONE,DEFAULT_URL);
}

public static final String DEFAULT_URL = "http://localhost:8761"+DEFAULT_PREFIX+"/";
public static final String DEFAULT_ZONE = "defaultZone";

将应用注册到对应的Eureka服务端中:

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

当构建了高可用的服务注册中心集群时,可以为参数的value值配置多个注册中心的地址(通过逗号分隔),比如:

eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/

味蕾服务注册中心的安全考虑,很多时候我们都会为服务注册中心加入安全校验。这个时候,在配置serviceUrl时,需要在value值的URL中加入相应的安全校验信息,比如:http://:@localhost:1111/eureka。其中为前端检验信息的用户名,为该用户的密码。

实现服务注册中心的安全校验:
先添加:compile ‘org.springframework.boot:spring-boot-starter-security’ 依赖;
然后在eureka server的application.yml中增加:

 secyrity: 
	basic:
		enabled:true
	user:
		name: test #用户名
		password:123456 #密码       

这样就添加了1个用户名及密码(注:其原理就是spring-security,熟悉spring-security的朋友,也可以改成把用户名/密码存储在数据库中)

服务实例类配置

可以查看org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean的源码来获取详细内容,这些配置信息都以eureka.instance为前缀。

元数据

在org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean的配置信息中,有一大部分内容都是对服务实例元数据的配置,服务实例的元数据是Eureka客户端在向服务诸恶中心发送注册请求时,用来描述自身服务信息的对象,其中包含了一些标准化的元数据,比如服务名称、实例名称、实例IP、实例端口等用于服务治理的重要信息;以及一些用于负载均衡策略或是其他特殊用途的自定义元数据信息。

在使用Spring Cloud Eureka的时候,所有的配置信息都通过org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean进行加载,但在真正进行服务注册的时候,还是会包装成com.netflix.appinfo.InstanceInfo对象发送给Eureka服务端。这两个类定义非常相似,我们可以直接查看com.netflix.appinfo.InstanceInfo类中的详细定义来了解原生Eureka对元数据的定义。其中,Map<String,String> metadata = new ConcurrentHashMap<String,String>()是自定义的元数据信息,而其他成员变量则是标准化的元数据信息。Spring Cloud的EurekaInstanceConfigBean对原生数据对象做了一些配置优化处理。

我们可以通过eureka.instance.=的格式对标准化元数据直接进行配置,其中就是EurekaInstanceConfigBean对象中的成员变量名。而对于自定义的元数据,可以通过eureka.instance.metadataMap.=的格式来进行配置:

eureka.instance.methdataMap.zone=shanghai

实例名配置

实例名,即InstanceInfo中的instanceId参数,它是区分统一服务中不同实例的唯一标识。在Netflix Eureka的原生实现中,实例名采用主机名作为默认值,这样的设置使得在同一主机上无法启动多个相同的服务实例。所以,在Spring Cloud Eureka的配置中,这对同一主机中启动多实例的情况,对实例名的默认命名做了更为合理的扩展,它采用了如下默认规则:

${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.intance_id:${server.port}}

对于实例名的命名规则,我们可以通过eureka.instance.instanceId参数来进行配置。本地调试时,需要启动同一服务的多个实例,可以在命令行中指定不同的server.port来启动,但略显麻烦。实际上,可以直接通过设置server.port=0或者使用随机数server.port=${random.int[10000,19999]}。这时注册到Eureka Server的实例名都是相同的,这会使得只有一个服务实例能够正常提供服务。这时,我们就通过设置实例名规则来解决:

eureka.instance.instanceId=${spring.application.name}:{random.int}

通过上面的配置,采用应用名加随机数的方式来区分不同的实例,从而实现在同一主机上,不指定端口就能轻松启动多个实例的效果(实际使用中只有server.port=0时,才会出现实例名相同的情况)。

端点配置

在InstanceInfo中,我们可以看到一些URL的配置信息,比如homePageUrl、statusPageUrl、healthCheckUrl,它们分别代表了应用主页的URL、状态页的URL、健康检查的URL。其中,状态页和健康检查的URL在Spring Cloud Eureka中默认使用了spring-boot-acturator模块提供的/info端点和/health端点。为了服务的正常运作,我们必须确保Eureka客户端的/health端点在发送元数据的时候,是一个能够被注册中心访问到的地址,否则服务注册中心不会根据应用的健康检查来更改状态(仅当开启了healthcheck功能时,以该端点信息作为健康检查标准)。而/info端点如果不正确的话,会导致在Eureka面板中单击服务实例时,无法访问到服务实例提供的信息接口。

大多数情况,我们并不需要修改这几个URL的配置,但是在一些特殊情况下,比如,为应用设置了context-path,这时,所有spring-boot-actuator魔窟啊的监控端点都会增加一个前缀。所以,我们就需要做类似如下的配置:

management.context-path=/hello
eureka.instance.statusPageUrlPath=${management.context-path}/info
eureka.instance.healthCheckUrlPath=${management.context-path}/health

有时候为了安全考虑,也有可能会修改/info和/health端点的原始路径。这时候,我们也需要做一些特殊的配置,例如:

endpoints.info.path=/appInfo
endpoints.health.path=/checkHealth

eureka.instance.statusPageUrlPath=/${endpoints.info.path}
eureka.instance.healthCheckUrlPath=/${endpoints.health.path}

上面举例的eureka.instance.statusPageUrlPath和eureka.instance.healthCheckUrlPath参数,这两个配置值有一个共同特点,它们都使用相对路径来进行配置。由于Eureka的服务注册中心默认会以HTTP的方式来访问和暴露这些端点,因此当客户端应用以HTTPS的方式来暴露服务和监控端点时,相对路径的配置方式就无法满足要求。Spring Cloud Eureka还提供了绝对路径的配置参数:

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

健康监测

默认,Eureka中各个服务实例的健康监测并不是通过spring-boot-actuator模块的/health端点来实现的,而是依靠客户端心跳的方式来保持服务实例的存活。在Eureka的服务续约与剔除机制下,客户端的健康状态从注册到注册中心开始都会处于UP状态,除非心跳终止一段时间后,服务注册中心将其剔除。默认的心跳实现方式可以优先检查客户端进程是否正常运作,但无法保证客户端应用能够正常提供服务。

在Spring Cloud Eureka中,我们可以通过简单的配置,把Eureka客户端的健康监测交给spring-boot-actuator模块的/health端点:

  • 在pom.xml中引入spring-boot-starter-actuator模块的依赖。
  • 在application.properties中增加参数配置eureka.client.healthcheck.enabled=true
  • 如果客户端的/health端点路径做了特殊处理,则需要按照上面介绍的配置进行修改。

其他配置

除了上面介绍的配置参数外,一些org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean中定义的配置参数以及对应的说明和默认值,这些参数均以eureka.instance为前缀。

这里写图片描述

跨平台支持

Eureka的通信机制使用了HTTP的REST接口实现,这也是Eureka同其他服务注册工具的一个关键不同。由于HTTP的平台无关性,虽然Eureka Server通过Java实现,但是在其他微服务应用并不限于使用Java来进行开发。

目前除了Eureka的Java客户端之外,还有很多其他语言平台对其的支持,比如eureka-js-client,python-eureka等。

通信协议

默认,Eureka使用Jersey和XStream配合JSON作为Server与Client之间的通信协议,也可以选择实现自己的协议来代替。

  • Jersey 是JAX-RS的参考实现,它包含三个主要部分。
    • 核心服务器(Core Server):通过提供JSR 311中标准化的注释和API标准化,可以用直观的方式开发RESTful Web服务。
    • 核心客户端(Core Client):Jersey客户端API帮助你与REST服务轻松通信。
    • 集成(Integration):Jersey还提供乐意轻松集成Spring、Guice、Apache Abdera的库。
  • XStream是用来将对象序列化成XML(JSON)或反序列化为对象的一个Java类库。

JAX-RS是将Java EE 6中引入的一种新技术。JAX-RS即Java API for RESTful Web Services,是一个Java编程语言的应用程序接口,支持按照表述性状态转移(REST)架构风格创建Web服务。JAX-RS使用了Java SE5引入的Java标注来简化Web服务的客户端和服务端的开发和部署。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值