Spring Cloud Consul 多实例注册问题

最近使用consul注册实例时发现一个问题,困扰了两天,终于解决记录下,同时也希望能对其他学习Spring Cloud有此类问题的“学友”有所帮助。

问题描述

例如两台服务器同一个应用config,端口8080,注册到consul上,就会出现后注册覆盖前一个注册的情况,产生这个问题的原因是consul在注册时的instanceid采用的是“服务名+端口”的方式,默认情况下是"spring.application.name-server.port"。

借鉴

那么如何解决这个问题呢,网上搜索了好多资料,其中有一篇“Spring Cloud Finchley版中Consul多实例注册的问题处理”这片文章帮助还是挺大的,下面结合这篇文章谈谈如何实现的。

解决方式

  • 方法一
    在Spring Cloud Consul 官网文档中有一段说明:

By default a consul instance is registered with an ID that is equal to its Spring Application Context ID. By default, the Spring Application Context ID is ${spring.application.name}:comma,separated,profiles: ${server.port}. For most cases, this will allow multiple instances of one service to run on one machine. If further uniqueness is required, Using Spring Cloud you can override this by providing a unique identifier in spring.cloud.consul.discovery.instanceId. For example:

application.yml

spring:
  cloud:
    consul:
      discovery:
        instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

意思是默认情况下consul注册的实例ID采用的是Spring Context ID,也就是spring application name-server.port,若进一步确定唯一性可以使用唯一标识覆盖spring.cloud.consul.discovery.instanceId。
具体实现:

spring.cloud.consul.discovery.instance-id = ${spring.application.name}-${server.port}-${random.int[1,999]}

这样的设置,每次服务重启注册都不一样,查看consul会有很多旧的实例不可用,个人感觉这种方式不太好。

  • 方法二(个人推荐)
    通过重写ConsulServiceRegistry的register方法进行修改
/**
 * @author : Erick
 * @version : 1.0
 * @Description :
 * @time :2018-11-26
 */
public class GatewayRegister extends ConsulServiceRegistry {

    public GatewayRegister(ConsulClient client, ConsulDiscoveryProperties properties, TtlScheduler ttlScheduler, HeartbeatProperties heartbeatProperties) {
        super(client, properties, ttlScheduler, heartbeatProperties);
    }

    @Override
    public void register(ConsulRegistration reg) {
    	//重新设计id,此处用的是名字也可以用其他方式例如instanceid、host、uri等
        reg.getService().setId(reg.getService().getName()+"-"+reg.getService().getAddress()+"-"+reg.getPort());
        super.register(reg);
    }
}

最主要的代码已经实现了,这也是在本文前边说的一篇文章的内容,如果写到这,重启服务去查看consul发现还是没有变化,仍然采用的是:spring.application.name-server.port进行注册的,那么如何将重写的方法真正的用起来呢,自己找的时候也是一头雾水毕竟是刚学习Spring Cloud 很多特性还不是很了解,带着疑问源码中ConsulServiceRegistry是如何进行使用的呢,全局搜索(包括jar)ConsulServiceRegistry,最后发现在ConsulServiceRegistryAutoConfiguration类中有这么一段代码:

@Bean
@ConditionalOnMissingBean
public ConsulServiceRegistry consulServiceRegistry(ConsulClient consulClient, ConsulDiscoveryProperties properties,
													   HeartbeatProperties heartbeatProperties) {
		return new ConsulServiceRegistry(consulClient, properties, ttlScheduler, heartbeatProperties);
}

那么我自己能不能按照这个方式将重写的consulRegister用起来呢,于是在启动类中增加了类似的代码:

@Bean
public GatewayRegister consulServiceRegistry(ConsulClient consulClient, ConsulDiscoveryProperties properties,
                                                 HeartbeatProperties heartbeatProperties) {
        return GatewayRegister(consulClient, properties, ttlScheduler, heartbeatProperties);
}

增加后发现ttlScheduler参数报错需要指定一个,接着看ConsulServiceRegistryAutoConfiguration中是如何使用的,发现只是注解引用,也加到启动类中,完整代码:

/**
 * @Description :
 * @Param
 * @author : Erick
 * @version : 1.0
 * @Date : 2018-10-26
 */
@SpringBootApplication
public class SpringcloudGatewayApplication {
	//参照源码定义声名
    @Autowired(required = false)
    private TtlScheduler ttlScheduler;
	//重写register方法
    @Bean
    public GatewayRegister consulServiceRegistry(ConsulClient consulClient, ConsulDiscoveryProperties properties,
                                                 HeartbeatProperties heartbeatProperties) {
        return new GatewayRegister(consulClient, properties, ttlScheduler, heartbeatProperties);
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringcloudGatewayApplication.class, args);
    }
}

再次启动查看consul注册服务发现成功了变为“spring.application.name- ip地址-server.port”了,至此重写register方法解决多实例注册问题。


Spring Cloud Consul 多实例注册问题_第1张图片

你可能感兴趣的:(Spring,Cloud,Spring,Cloud,基础教程)