Spring-Cloud服务在Consul中的异常注册

[原创]个人理解,请批判接受,有误请指正。转载请注明出处: https://heyfl.gitee.io/Bug-Log-Optimization/bug_in_spring-cloud_instance_registered_with_consul.html


优雅停机脚本见: shell-系统优雅停机

背景

公司实现微服务化并原来使用的Dubbo+Zookeeper实现应用间的服务调用,考虑到Dubbo不在维护最近想要切换为Spring Cloud+Consul

环境

  • Spring Cloud: Edgware.SR3
  • Spring-boot: 1.5.13.RELEASE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

根据网上流传的博客我们使用的配置为:

1
spring.cloud.consul.discovery.instance-id=${spring.application.name}-${server.port}

也就是【服务名】+【端口】的形式来标识Consul上的一个服务



1. 第一个问题

已注册上Consul的服务被后启动的提供者覆盖(采用【服务名】+【端口】的形式)

在我们实际开发测试时候发现 无论服务起了多少个实例,最终展示到Consul都只有一个 :

服务名+端口
怀疑是Consul以【服务名】+【端口】为实例的唯一标示,导致【后起来的服务】覆盖掉【原来已经注册到Consul上的服务】了 ,我们通过Feign调用时发现也确实是如此

1.1. Kill 第一个问题

Spring官方文档轻松找到解决方案(有问题还是官方文档好)

1
${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

原理就是每次启动时注册的实例ID都为【服务名】+【随机数】:

服务名+随机数
通过这种形式,可以让每一个服务提供者(实例)都有效地注册到Consul上,并且Consul上的每一个Instance都能唯一映射到每一个提供者上

2. 第二、第三个问题

2.1. 服务器通过Kill -9的重启脚本快速重启导致一个提供者在Consul上注册了多次(Consul注册的实例数>实际提供者数)

首先,我们要知道当系统执行kill-9命令的时候会立马强制关闭该进程,程序很可能正在处理请求中,同时也占用了一些的资源,本来需要做一些善后才能正常、安全的结束,但是你一个 kill-9命令过来,程序就措手不及了。
结合上述情况,实际上程序非正常重启,已经注册在consul上的服务没有被反注册,服务重新启动之后又重新的注册了一个新的服务上去了(而且重启后以前的服务在Consul心跳机制下海认为是可用的),一个服务就在Consul注册了多次了

一个服务非法重启后在Consul注册了多个实例

这种情况可以修改重启脚本解决:
通过Kill -15直接关闭提供者进程  可以参考这篇文章

2.2. 【服务名】+【随机数】的InstanceID不能提供足够信息帮助我们快速定位问题

上面的方案可以解决我们遇到的问题,但是有一点不足的是,Consul上看到的InstanceID都是【服务名】+【随机数】,随机数没有可读性可言,
我们压根不能根据这个InstanceID一眼看出的它的服务提供者是谁。不便于我们排查问题

进一步优化

其实最好就是使用【服务名】+【机器IP】+【端口】的形式,这样既能通过唯一标示服务提供者解决 Consul服务被覆盖的问题 也能方便我们运维开发时快速知道当前服务的提供者列表,快速定位问题
最终方案

1
spring.cloud.consul.discovery.instance-id=${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
服务名+IP+端口

作者

神奇宝贝大师

发布于

2018-10-02

更新于

2021-12-10

许可协议

评论