天天看点

SpringCloud服务发现组件Eureka常见问题汇总

在《SpringCloud服务发现组件Eureka实践》一文中,主要讲述了在生产环境中部署Eureka所涉及的相关实践操作。而在本文中,笔者将结合上文的内容,对部署Eureka集群后的使用过程中遇到的问题进行汇总,便于日后遇到相关问题时进行查看。

一.注册服务慢

1.背景

默认情况下,服务启动后,需要注册到Eureka Server上。但是在开发过程中发现,服务注册到Eureka Server的速度较慢。因此,在开发过程中希望缩短服务的注册时间以提高工作效率。

2.原因

服务注册涉及到周期性心跳,默认30秒一次。只有当实例,服务器端和客户端的本地缓存中的元数据都相同时,服务才能被其他客户端发现(所以可能需要3次心跳)。原理如下图所示:

SpringCloud服务发现组件Eureka常见问题汇总

因此,可以通过缩短客户端的等待时间间隔,也就是缩短客户端向Eureka发送心跳的时间间隔来提高工作效率。在Eureka中存在参数eureka.instance.leaseRenewalIntervalInSeconds,该值用于设置Eureka Client向Eureka Server发送心跳的时间间隔,默认30秒。通过将该参数设置成一个更小的值来实现。

3.解决方案

eureka.instance.leaseRenewalIntervalInSeconds设置成一个更小的值即可。

eureka:
  instance:
    lease-renewal-interval-in-seconds: 10
           

4.注意事项

生产环境下,该值建议使用默认值,以保证客户端可用。

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

1.背景

在开发环境下,我们会希望Eureka Server能迅速注销已停止的微服务实例。但是在实际开发过程中,往往当微服务停掉或者挂掉后,该服务实例仍然注册在Eureka Server上,需要等待很长一段时长后才消失。

2.原因

幕后的这一切,归结于两个方面:

一方面是因为Eureka Server清理无效节点的周期长(默认90秒);

另一方面,Eureka开启了自我保护模式之后,甚至不会注销任何微服务;

3.解决方案

了解原因之后,可以从上述两方面着手解决:

针对第一种情况,解决方案如下:

在Eureka Client端配置开启健康检查,并配置较短的续约更新时间和较短的到期时间。

eureka:
  client:
    healthcheck:
      enabled: true //开启健康检查
  instance:
    lease-renewal-interval-in-seconds: 10    //配置续约更新时间
    lease-expiration-duration-in-seconds: 10   //配置续约到期时间
           

针对第二种情况,解决方案如下:

在Eureka Server端配置关闭自我保护,并配置较短的Eureka Server清理无效节点的时间间隔。

eureka:
  server:
    enable-self-preservation: false    //关闭自我保护
    eviction-interval-timer-in-ms: 4000    //清理无效节点的时间间隔
           

4.注意事项

这些配置建议在开发或测试时使用,生产环境建议使用默认值。

5.扩展方案

如果读者了解Eureka Server的高级特性,那么在该问题下,也可以通过手动方式解决。

Eureka提供了一些REST端点来供我们进行服务的注册管理。非JVM的微服务可使用这些REST端点操作Eureka,从而实现注册与发现。

另外,Eureka的REST端点可以使用XML或者JSON与这些端点通信(默认是XML),来控制微服务的注册上下线。

因此,在本问题下,可以使用REST端点的删除端点进行删除。例如下列的微服务,如何做下线操作呢?

SpringCloud服务发现组件Eureka常见问题汇总

可以通过发送下列请求进行删除:

http://${user}:${password}@localhost:8761/eureka/apps/${application_name}/${server_name}
           

其中,该请求为DELETE方式,user:password为Eureka认证的账号密码;

application_name为Application的名称;

server_name为具体Application下某个Server的名称;

因此,上面的例子的下线方式如下:

http://admin:[email protected]:8761/eureka/apps/ZZ-PROVIDER-USER/windows10.microdone.cn:zz-provider-user:8001
           

三.自定义微服务的InstanceID

1.背景

在Eureka的已注册服务界面中,希望自定义微服务的InstanceID,而不是默认值。

2.原因

在SpringCloud中,服务的InstanceId默认值是:

${spring.cloud.clent.hostname}:${spring.cloud.application.name}:${spring.application.port}
           

3.解决方案

只需要在微服务中配置eureka.instance.instance-id属性即可。

eureka:
   instance:
      instance-id: ${spring.cloud.client.ipAddress}:${server.port}
           

四.名称异常(UNKNOWD)

1.背景

在Eureka Server的注册界面上,显示微服务名称为UNKNOWN,或者应用状态为UNKNOWN。

当微服务名称为UNKNOWN发生时,显然不符合科学。首先微服务的名称不够语义化,无法看出是哪个微服务,其次我们常常使用应用名称消费对应微服务的接口,这样会导致消费的失败。

而当应用状态为UNKNOWN时,微服务不是UP状态(也即是不健康状态),同时会导致消费的失败。

2.原因

以下分两种情况讨论

<1>应用名称为UNKNOWN的情况

一般来说有两种情况会导致该问题的发生:

1~未配置spring.application.name或者eureka.instance.appname属性。如果这两个属性均不配置,则会导致应用UNKNOWN的问题;

2~某些版本的SpringFox会导致该问题。例如SpringFox2.6.0。建议使用SpringFox2.6.1或更新的版本;

<2>微服务实例状态为UNKNOWN的情况

该问题一般由健康检查导致。

3.解决方案

针对第一种情况,可以配置spring.application.name或者eureka.instance.appname属性:

spring:
  application:
    name: zz-provider-user 
           

针对第二种情况,可以配置eureka.client.healthcheck.enabled属性:

eureka:
  client:
    healthcheck:
      enabled: true
           

注意:该种情况下,eureka.client.healthcheck.enabled=true必须设置在application.yml中,而不能设置在boostrap.yml,否则一些场景下会导致应用状态UNKNOWN的问题。

五.扩展Eureka客户端元数据

1.背景

在Eureka使用过程中,我们常常需要根据自己定义的元数据,来区分不同的微服务。举个栗子,在测试环境的Eureka集群上注册了2个同样的微服务A,B,我们希望在SIT环境消费微服务时只消费到微服务A,而在UAT环境消费时只消费到微服务B,此时通过自己定义的元数据,区分不同环境在消费微服务时的选择。原理图如下:

SpringCloud服务发现组件Eureka常见问题汇总

2.相关思路

通过元数据,可以解决这类问题。那么什么是元数据呢?下面简单介绍下Eureka的元数据。

Eureka的元数据包括标准元数据和自定义元数据。

标准元数据指的是主机名,IP地址,端口号,状态页和健康检查等信息。这些信息都会被发布在服务注册表中,用于服务之间的调用;

自定义元数据可以使用eureka.instance.metadata-map配置,这些数据可以在远程客户端中访问,但是并不会改变客户端的行为;

通过使用DiscoveryClient.getInstances(serviceId),可查询指定微服务在Eureka上的实例列表

3.解决方案

配置自定义元数据

<1>配置yml文件

eureka:
  client:
    serviceUrl:
      defaultZone: http://admin:[email protected]:8761/eureka/,http://admin:[email protected]:8761/eureka/
  instance:
    prefer-ip-address: true
    metadata-map:
      service-id: 10000
      service-timeout: 60
           

<2>新增UserController

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @RequestMapping("/getUserInfo")
    public List<ServiceInstance> getUserInfo () {
        return discoveryClient.getInstances("zz-provider-user");
    }
    
}
           

<3>访问元数据

启动应用,通过user/getUserInfo访问。

通过上述步骤实现自定义元数据,解决Eureka分组的问题。除此之外,自定义元数据还可以解决其他场景的问题,例如微服务建立唯一标识,微服务消费排序等问题,在此不再赘述。

六.多网卡环境下的IP选择

1.背景

某些微服务所部署的服务器具有多个网卡,这些微服务在注册到Eureka Server时,如果按照默认的注册流程,可能导致该微服务无法被其他服务器访问。

例如某些服务器有eth0,eth1和eth2三张网卡,但是只有eth1可以被其他的服务器访问;如果Eureka Client将eth0或者eth2注册到Eureka Server上,其他微服务就无法通过这个IP调用该微服务的接口。

2.解决方案

针对多网卡的服务器,可以通过指定各个微服务注册到Eureka Server上的IP来实现。

Spring Cloud提供了按需选择IP的能力,从而避免以上的问题。下面提供SpringCloud自带的四种解决方案。

<1>忽略指定名称的网卡:

spring:
  cloud:
    inetutils:
      ignored-interfaces:
        - dokcer0
        - veth.*
eureka:
  instance:
    prefer-ip-address: true
           

这样就可以忽略docker0网卡以及所有以veth开头的网卡。

<2>使用正则表达式,指定使用的网络地址:

spring:
  cloud:
    inetutils:
      preferredNetworks:
        - 192.168
        - 10.0
eureka:
  instance:
    prefer-ip-address: true
           

<3>只使用站点本地地址:

spring: 
  cloud: 
    inetutils: 
      useOnlySiteLocalInterfaces: true
           

<4>手动指定IP地址:

eureka: 
  instance: 
    prefer-ip-address: true
      ip-address: 127.0.0.1
           

继续阅读