服务自动注册consul

之前项目使用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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容