Spring Boot Admin手动配置IP注册实例

功能需求:刚开始功能是让SpringBootAdmin将两个无关的naocs中的服务全部展示出来,发现SpringBootAdmin只会选择一个nacos中的服务展示,所以没走通。沟通后改为在服务端配置要监控的服务的IP端口,将这些服务手动注册进去。

实现方案:在看SpringBootAdmin client端注册流程的时候,发现是通过RegistrationApplicationListener去注册实例。所以我们可以仿造这个类去注册我们的实例。

1.在服务端配置文件中配置IP:

2.在服务端编写配置文件对应的Properties类

package com.itnode.springbootadmin.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@ConfigurationProperties(prefix = "server")
@Configuration
public class ServerListProperties {
    private List<String> serverAddr;

    public List<String> getServerAddr() {
        return serverAddr;
    }

    public void setServerAddr(List<String> serverAddr) {
        this.serverAddr = serverAddr;
    }
}

3. 在服务端编写手动注册IP的类RegistrationApplicationListener(可仿造源码的RegistrationApplicationListener类进行修改),以下为我的注册流程,可根据自己需求自行更改。

/*
 * Copyright 2014-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.itnode.springbootadmin.listener;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.itnode.springbootadmin.config.ServerListProperties;
import de.codecentric.boot.admin.client.config.InstanceProperties;
import de.codecentric.boot.admin.client.registration.Application;
import de.codecentric.boot.admin.client.registration.ApplicationFactory;
import de.codecentric.boot.admin.client.registration.ApplicationRegistrator;
import de.codecentric.boot.admin.server.domain.values.InstanceId;
import de.codecentric.boot.admin.server.domain.values.Registration;
import de.codecentric.boot.admin.server.services.InstanceRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.annotation.Order;
import org.springframework.http.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.client.RestTemplate;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.LongAdder;

/**
 * Listener responsible for starting and stopping the registration task when the application is
 * ready.
 *
 * @author Johannes Edmeier
 */
public class RegistrationApplicationListener implements InitializingBean, DisposableBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationApplicationListener.class);
    private final ApplicationRegistrator registrator;
    private final ThreadPoolTaskScheduler taskScheduler;
    private boolean autoDeregister = false;
    private boolean autoRegister = true;
    private Duration registerPeriod = Duration.ofSeconds(10);//任务间隔

    private volatile ScheduledFuture<?> scheduledTask;
    private final ServerListProperties serverList;//配置的ip列表
    private final ApplicationFactory applicationFactory;
    private final ConcurrentHashMap<String, LongAdder> attempts = new ConcurrentHashMap<>();
    private final AtomicReference<String> registeredId = new AtomicReference<>();
    private final RestTemplate template;

    private final String ADMIN_URL = "http://localhost:9090/instances";
    private final String URL_PREFIX = "http://";
    private final InstanceRegistry registry;

    @Autowired
    private InstanceProperties instanceProperties;

    public RegistrationApplicationListener(ApplicationRegistrator registrator,
                                           ApplicationFactory applicationFactory,
                                           ServerListProperties serverList, RestTemplate template,
                                           InstanceRegistry registry) {
        this(registrator, registrationTaskScheduler(), applicationFactory, serverList, template, registry);

    }

    private static ThreadPoolTaskScheduler registrationTaskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setThreadNamePrefix("registrationTasks");
        return taskScheduler;
    }

    RegistrationApplicationListener(ApplicationRegistrator registrator, ThreadPoolTaskScheduler taskScheduler,
                                    ApplicationFactory applicationFactory, ServerListProperties serverList,
                                    RestTemplate template, InstanceRegistry registry) {
        this.registrator = registrator;
        this.taskScheduler = taskScheduler;
        this.applicationFactory = applicationFactory;
        this.serverList = serverList;
        this.template = template;
        this.registry = registry;
    }

    @EventListener
    @Order(Ordered.LOWEST_PRECEDENCE)
    public void onApplicationReady(ApplicationReadyEvent event) {
        if (autoRegister) {
            startRegisterTask();
        }
    }

    public void startRegisterTask() {
        if (scheduledTask != null && !scheduledTask.isDone()) {
            return;
        }
        for (String url : serverList.getServerAddr()) {
            scheduledTask = taskScheduler.scheduleAtFixedRate(() -> register(url), registerPeriod);
        }
        LOGGER.debug("Scheduled registration task for every {}ms", registerPeriod);
    }

    public boolean register(String url) {
        boolean isRegistrationSuccessful = false;
        String applicationName = getName(url);
        if (applicationName != null) {
            instanceProperties.setName(applicationName);
        }else {
            return false;
        }
        instanceProperties.setManagementUrl(URL_PREFIX + url + "/actuator");
        instanceProperties.setHealthUrl(URL_PREFIX + url + "/actuator/health");
        instanceProperties.setServiceUrl(URL_PREFIX + url + "/");
        //创建应用
        Application self = createApplication();
        //attempt记录注册尝试次数
        LongAdder attempt = this.attempts.computeIfAbsent(ADMIN_URL, k -> new LongAdder());
        boolean successful = register(self, ADMIN_URL, attempt.intValue() == 0);
        if (!successful) {
            attempt.increment();
        } else {
            attempt.reset();
            isRegistrationSuccessful = true;
        }
        return isRegistrationSuccessful;
    }

    //远程调用actuator接口获取应用名
    private String getName(String url) {

        if (url.startsWith("172")) {
            ResponseEntity<String> nacosforEntity = template.getForEntity(URL_PREFIX + url + "/actuator/nacos-discovery", String.class);
            if (nacosforEntity.getStatusCodeValue() == 200) {
                JSONObject jsonObject = JSON.parseObject(nacosforEntity.getBody());
                JSONObject nacosDiscoveryProperties = (JSONObject) jsonObject.get("NacosDiscoveryProperties");
                return (String) nacosDiscoveryProperties.get("service");
            }
        } else {
            ResponseEntity<String> forEntity = template.getForEntity(URL_PREFIX + url + "/actuator/health", String.class);
            if (forEntity.getStatusCodeValue() == 200) {
                JSONObject jsonObject = JSON.parseObject(forEntity.getBody());
                JSONObject components = (JSONObject) jsonObject.get("components");
                JSONObject discoveryComposite = (JSONObject) components.get("discoveryComposite");
                JSONObject discoveryCompositeComponents = (JSONObject) discoveryComposite.get("components");
                JSONObject discoveryClient = (JSONObject) discoveryCompositeComponents.get("discoveryClient");
                JSONObject details = (JSONObject) discoveryClient.get("details");
                JSONArray services = (JSONArray) details.get("services");
                return (String) services.get(0);
            }
        }

        return null;
    }

    protected Application createApplication() {
        return applicationFactory.createApplication();
    }

    protected boolean register(Application self, String adminUrl, boolean firstAttempt) {
        try {
            //构造一个Registration对象
            Registration registration = Registration.builder().name(self.getName()).source("http-api")
                    .healthUrl(self.getHealthUrl()).managementUrl(self.getManagementUrl())
                    .metadata(self.getMetadata())
                    .serviceUrl(self.getServiceUrl()).build();

            //调用InstanceRegistry的register方法注册进去
            Mono<InstanceId> register = registry.register(registration);
            InstanceId instanceId = register.block();
//            ResponseEntity<Map<String, Object>> response = this.template.exchange(adminUrl, HttpMethod.POST, new HttpEntity(self, HTTP_HEADERS), RESPONSE_TYPE, new Object[0]);
//            if (response.getStatusCode().is2xxSuccessful()) {
            if(instanceId!=null){
                if (this.registeredId.compareAndSet( null, instanceId.toString())) {
                    LOGGER.info("Application registered itself as {}", instanceId.getValue());
                } else {
                    LOGGER.debug("Application refreshed itself as {}", instanceId.getValue());
                }
                return true;
            }

            if (firstAttempt) {
                LOGGER.warn("Application failed to registered itself as {}. Response: {}. Further attempts are logged on DEBUG level", self, register.toString());
            } else {
                LOGGER.debug("Application failed to registered itself as {}. Response: {}", self, register.toString());
            }
        } catch (Exception var5) {
            if (firstAttempt) {
                LOGGER.warn("Failed to register application as {} at spring-boot-admin ({}): {}. Further attempts are logged on DEBUG level", new Object[]{self, adminUrl, var5.getMessage()});
            } else {
                LOGGER.debug("Failed to register application as {} at spring-boot-admin ({}): {}", new Object[]{self, adminUrl, var5.getMessage()});
            }
        }

        return false;
    }

    @Override
    public void afterPropertiesSet() {
        taskScheduler.afterPropertiesSet();
    }

    @Override
    public void destroy() {
        taskScheduler.destroy();
    }

}

4. 在服务端将RegistrationApplicationListener注入到Spring中

@Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Bean("myRegistrationApplicationListener")
    public RegistrationApplicationListener registrationListener(ApplicationFactory applicationFactory,
                                                                ServerListProperties serverList, RestTemplate template,
                                                                ApplicationRegistrator registrator,
                                                                InstanceRegistry registry) {
        RegistrationApplicationListener listener = new RegistrationApplicationListener(registrator,applicationFactory,serverList,template,registry);
        return listener;
    }

 5. pom需要引入SpringBootAdmin中client和server的依赖

<dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>${spring-boot-admin.version}</version>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>${spring-boot-admin.version}</version>
        </dependency>

本人还在上学小菜鸡一枚,如有错误地方或任何建议都可提出,看见后会予以纠正。感谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值