Springboot使用redis发布订阅实时获取多节点服务器资源信息

引入pom

<dependency>
    <groupId>com.github.oshi</groupId>
    <artifactId>oshi-core</artifactId>
    <version>3.5.0</version>
</dependency>

1.创建OS工具类

import lombok.extern.slf4j.Slf4j;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;

import java.math.RoundingMode;
import java.text.DecimalFormat;

@Slf4j
public class OSUtils {

    private static final SystemInfo SI = new SystemInfo();
    private static HardwareAbstractionLayer hal = SI.getHardware();
    public static final String TWO_DECIMAL = "0.00";

    /**
     * whether is windows
     * @return true if windows
     */
    public static boolean isWindows() {
        return getOSName().startsWith("Windows");
    }

    /**
     * get current OS name
     * @return current OS name
     */
    public static String getOSName() {
        return System.getProperty("os.name");
    }


    /**
     * get memory usage
     * Keep 2 decimal
     * @return  percent %
     */
    public static String memoryUsage() {
        GlobalMemory memory = hal.getMemory();
        double memoryUsage = (memory.getTotal() - memory.getAvailable() ) * 0.1 / memory.getTotal() * 10*100;

        DecimalFormat df = new DecimalFormat(TWO_DECIMAL);
        df.setRoundingMode(RoundingMode.HALF_UP);
        return df.format(memoryUsage);
    }


    /**
     * load average
     *
     * @return load average
     */
    public static String loadAverage() {
        double loadAverage =  hal.getProcessor().getSystemLoadAverage(2)[1];
        DecimalFormat df = new DecimalFormat(TWO_DECIMAL);

        df.setRoundingMode(RoundingMode.HALF_UP);
        return df.format(loadAverage);
    }


    /**
     * get cpu usage
     *
     * @return cpu usage
     */
    public static String cpuUsage() {
        CentralProcessor processor = hal.getProcessor();
        double cpuUsage = processor.getSystemCpuLoad()*100;

        DecimalFormat df = new DecimalFormat(TWO_DECIMAL);
        df.setRoundingMode(RoundingMode.HALF_UP);

        return df.format(cpuUsage);
    }



}

IP工具类

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;

public class IpUtils {

    /**
     * 获取请求真实ip地址,避免获取代理ip
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ipAddress= Objects.requireNonNull(inet).getHostAddress();
            }
        }
        //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
            if(ipAddress.indexOf(",")>0){
                ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }

    /**
     * get local host
     * @return host
     */
    public static String getHost(){

        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return "127.0.0.1";
    }


    public static void main(String[] args) {
        System.out.println(getHost());
    }
}

 

 2.创建监控对象

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ServerInfoVO {
    private String serverCode;

    private String serverIp;

    private String cpuUse;

    private String menUse;

    private String load;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date updateTime;
}

3.创建server启动监听

import com.alibaba.fastjson.JSONObject;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.ws.websocket.util.IpUtils;
import com.ws.websocket.util.OSUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class Server implements ApplicationRunner {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static final String CPU_HEART = "CPU_HEART";


    @Value("${xxl.job.executor.title}")
    private String title;

    @Value("${xxl.job.executor.port}")
    private String port;

    private void startup(){
        try {
            heartbeat();
            log.info("start Quartz server...");
        } catch (Exception e) {
            log.error("start Quartz failed : " + e.getMessage(), e);
        }
    }

    private void heartbeat() {
        ScheduledExecutorService heartbeatMasterService = newDaemonThreadScheduledExecutor("executor-heart-Thread",1);
        Runnable heartBeatThread = heartBeatThread();
        heartbeatMasterService.
                scheduleAtFixedRate(heartBeatThread, 1, 1, TimeUnit.MINUTES);
    }

    /**
     *  heartbeat thread implement
     * @return
     */
    private Runnable heartBeatThread(){
        log.info("start heart beat thread...");
        return () -> {

            ServerInfoVO serverInfoVO = ServerInfoVO.builder()
                    .serverIp(IpUtils.getHost()+"-"+port)
                    .serverCode(title)
                    .cpuUse(OSUtils.cpuUsage()+"%")
                    .menUse(OSUtils.memoryUsage()+"%")
                    .load(OSUtils.loadAverage())
                    .updateTime(new Date())
                    .build();
            stringRedisTemplate.convertAndSend(CPU_HEART,JSONObject.toJSONString(serverInfoVO));
        };
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        startup();
    }

    public static ScheduledExecutorService newDaemonThreadScheduledExecutor(String threadName, int corePoolSize) {
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setDaemon(true)
                .setNameFormat(threadName)
                .build();
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
        executor.setRemoveOnCancelPolicy(true);
        return executor;
    }
}


4.配置redis

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

/**
 * @Author: wangsheng
 * @Data: 2022/8/16 16:40
 */
@Slf4j
@Configuration
public class RedisListenerConfig {
    /**
     * 订阅操作频道
     *
     * @param connectionFactory connectionFactory
     * @return RedisMessageListenerContainer
     */
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                            MessageReceive listenerAdapter){
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter,new PatternTopic("CPU_HEART"));
        log.info(listenerAdapter.getClass().getName() + " 订阅频道 {}", "CPU_HEART");
        return container;
    }
}

5.订阅消息

package com.ws.websocket.config;


import com.alibaba.fastjson.JSON;
import com.ws.websocket.util.ServerHelper;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;


@Component
public class MessageReceive implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] bytes) {
        try {
            ServerInfoVO serverInfoVO = JSON.parseObject(message.toString(),ServerInfoVO.class);
            ServerHelper.put(serverInfoVO.getServerIp(),serverInfoVO);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
import com.ws.websocket.config.ServerInfoVO;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ServerHelper {

    private static final Map<String, ServerInfoVO> maps = new ConcurrentHashMap<>();


    public static void put(String key, ServerInfoVO serverInfoVO) {
        maps.put(key, serverInfoVO);
    }


    public static List<ServerInfoVO> get() {
        List<ServerInfoVO> list = new ArrayList<>();

        Collection<ServerInfoVO> collection = maps.values();
        for (ServerInfoVO serverInfoVO : collection) {
            serverInfoVO.setServerIp(null);
            long differ = new Date().getTime() - Objects.requireNonNull(serverInfoVO.getUpdateTime()).getTime();
            if (differ > 100000) {
                maps.remove(serverInfoVO.getServerCode());
                continue;
            }

            list.add(serverInfoVO);
        }
        return list;
    }

}


6.获取服务器资源信息

    public Object getResourceMonitor() {
        return ServerHelper.get();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今朝花落悲颜色

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值