之前项目使用docker部署服务 在将服务注册到consul 上,因各种原因会出现连接不上consul 等情况,直接上代码
ConsulRetryAutoConfiguration.java
import com.ecwid.consul.v1.ConsulClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryClient;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @Description: consul重试自动配置
* @Author: luozy
* @CreateDate: 2020/8/10 17:51
*/
@Configuration
@ConditionalOnClass({
ConsulDiscoveryClient.class,
ConsulAutoRegistration.class,
ConsulServiceRegistry.class,
ConsulClient.class
})
@Import({ConsulRetryProperties.class})
public class ConsulRetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean({ConsulRetryRegistry.class})
public ConsulRetryRegistry consulRetryRegistry(
ConsulAutoRegistration consulAutoRegistration,
ConsulServiceRegistry consulServiceRegistry,
DiscoveryClient discoveryClient,
ConsulRetryProperties properties,
ConsulClient client
) {
return new ConsulRetryRegistry(
consulAutoRegistration,
consulServiceRegistry,
discoveryClient,
properties,
client
);
}
}
重试配置
ConsulRetryProperties.java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @Description: consul重试配置
* @Author: luozy
* @CreateDate: 2020/8/10 17:51
*/
@Data
@Configuration
@ConfigurationProperties("spring.cloud.consul.retry")
public class ConsulRetryProperties {
/**
* 监测间隔(单位:ms)
*/
private long initialInterval = 10000L;
/**
* 间隔因子(备用)
*/
private double multiplier = 1.1D;
/**
* 最大间隔(备用)
*/
private long maxInterval = 20000L;
/**
* 重试次数(备用)
*/
private int maxAttempts = 6;
}
ConsulRetryRegistry.java 里面有 很多种 判断方法 可以自己选择,但是推荐用instanceId 的判断
我这是1.X的版本 service 中 ID 就是instanceId 不同版本请自己注意跟踪下代码查看
import cn.hutool.core.thread.ThreadUtil;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.NewService;
import com.ecwid.consul.v1.health.model.Check;
import com.ecwid.consul.v1.health.model.HealthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;
import java.util.List;
/**
* @Description: consul服务重新注册
* @Author: luozy
* @CreateDate: 2020/8/10 17:51
*/
@Slf4j
public class ConsulRetryRegistry implements CommandLineRunner {
private ConsulAutoRegistration consulAutoRegistration;
private ConsulServiceRegistry consulServiceRegistry;
private DiscoveryClient discoveryClient;
private ConsulRetryProperties properties;
private final ConsulClient client;
public ConsulRetryRegistry(
ConsulAutoRegistration consulAutoRegistration,
ConsulServiceRegistry consulServiceRegistry,
DiscoveryClient discoveryClient,
ConsulRetryProperties properties,
ConsulClient client
) {
this.consulAutoRegistration = consulAutoRegistration;
this.consulServiceRegistry = consulServiceRegistry;
this.discoveryClient = discoveryClient;
this.properties = properties;
this.client = client;
}
@Override
public void run(String... args) {
// 获取当前服务
final NewService service = this.consulAutoRegistration.getService();
final String serviceId = service.getName();
final String inservice = service.getId();
// 启动一个线程进行服务监测
ThreadUtil.newSingleExecutor().execute(
() -> {
log.info("consul服务监测已启动【{}】", service);
while (true) {
try {
Thread.sleep(this.properties.getInitialInterval());
} catch (InterruptedException e) {
// 当前线程异常退出
log.error("consul服务已停止重新注册【{}】", service);
break;
}
// 健康检查
if (!this.checkStatusForService(serviceId, inservice)) {
try {
// 重新注册
this.registry();
log.info("consul服务重新注册成功【{}】", service);
} catch (Exception e) {
log.warn("consul服务当前注册失败,准备下一次注册【{}】", service);
}
}
}
}
);
}
/**
* 服务注册
*/
private void registry() {
this.consulServiceRegistry.register(this.consulAutoRegistration);
}
/**
* 检测服务是否正常 ,此方法必须在保证服务启动健康检查能通过的情况下。如果健康检查URL 都不对那肯定是不正常的。
*
* @param serviceId 服务名
* @param instanceId 唯一标识
* @return
*/
private boolean checkStatusForService(String serviceId, String instanceId) {
try {
Response<List<HealthService>> response1 = client.getHealthServices(serviceId, false, QueryParams.DEFAULT);
List<HealthService> healthServiceList = response1.getValue();
for (HealthService healthService : healthServiceList) {
healthService.getService().getId();
if (instanceId.equals(healthService.getService().getId())) {
log.debug("consul服务心跳检测结束,检测结果为:{}", true ? "正常" : "异常");
return true;
}
}
} catch (Exception e) {
log.debug("consul服务心跳检测结束,检测结果为:{}", false ? "正常" : "异常");
//调用consul 失败则不注册等待 consul启动好 在注册
return true;
}
//如果未找到此服务 ,用则注册
log.debug("consul服务心跳检测结束,检测结果为:{}", false ? "正常" : "异常");
return false;
}
/**
* 检测服务是否正常 ,此方法必须在保证服务启动健康检查能通过的情况下。如果健康检查URL 都不对那肯定是不正常的。
*
* @param serviceId 服务名
* @param instanceId 唯一标识
* @return
*/
private boolean checkStatusForChecks(String serviceId, String instanceId) {
try {
Response<List<Check>> response = client.getHealthChecksForService(serviceId, QueryParams.DEFAULT);
List<Check> checks = response.getValue();
for (Check check : checks) {
if (check.getServiceId().equals(instanceId)) {
if (!check.getStatus().toString().equals(com.ecwid.consul.v1.agent.model.Check.CheckStatus.PASSING.toString())) {
//健康检查失败则重新注册
log.debug("consul服务心跳检测结束,检测结果为:{}", false ? "正常" : "异常");
return false;
} else {
//健康检查成功则不注册
log.debug("consul服务心跳检测结束,检测结果为:{}", true ? "正常" : "异常");
return true;
}
}
}
} catch (Exception e) {
log.debug("consul服务心跳检测结束,检测结果为:{}", false ? "正常" : "异常");
//调用consul 失败则不注册等待 consul启动好 在注册
return true;
}
//如果未找到此服务 ,用则注册
log.debug("consul服务心跳检测结束,检测结果为:{}", false ? "正常" : "异常");
return false;
}
/**
* 检测服务是否正常
*/
@Deprecated
private void checkStatus() {
Object object = this.consulServiceRegistry.getStatus(this.consulAutoRegistration);
System.out.println(object.toString());
}
/**
* 检查服务状态
*
* @param service 服务
* @return 返回布尔值,正常true,异常false
*/
@Deprecated
private boolean checkStatus(NewService service) {
// 检测标志
boolean flag = false;
try {
// 获取所有服务实例
List<ServiceInstance> instances = this.discoveryClient.getInstances(service.getName());
// 遍历实例
for (ServiceInstance instance : instances) {
// 判断是否为当前服务
// if (instance.getServiceId().equals(service.getName())) {
if (instance.getHost().equals(service.getAddress())) {
flag = true;
break;
}
}
}catch (Exception e){}
log.debug("consul服务心跳检测结束,检测结果为:{}", flag?"正常":"异常");
return flag;
}
}
consul 主要配置
spring:
application:
name: test
cloud:
consul:
host: 127.0.0.1
port: 8500
enabled: true
retry:
initial-interval: 10000 #最初重试间隔为 10000 毫秒
max-attempts: 100 #最多重试 100 次
max-interval: 50000 #最长重试间隔为 50000 毫秒
multiplier: 1.2 #每次重试失败后,重试间隔所增加的倍数
discovery:
enabled: true
register: true
instance-id: ${spring.application.name}-${random.uuid} #随机生成 instance-id 避免多副本情况下只有一个注册
prefer-ip-address: true #自动获取IP
health-check-path: ${server.servlet.context-path}/actuator/health #可自行配置
health-check-interval: 10s #重试时间
health-check-critical-timeout: 3s #失效后取消注册时间 建议必须配置,否则会出现多个无效的注册
config:
enabled: true #默认是true --
format: PROPERTIES # 表示consul上面文件的格式 有四种 YAML PROPERTIES KEY-VALUE FILES
data-key: configuration #表示consul上面的KEY值(或者说文件的名字) 默认是data
fail-fast: true
启动时 加载配置
image.png
#初始化
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xx.consul.ConsulRetryAutoConfiguration