netty 心跳机制实现服务端检测客户端内存使用情况用例

一,加入jar包依赖

<dependencies>
        <!-- Netty依赖包
        https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.25.Final</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.22</version>
        </dependency>
        <!--fst序列化-->
        <dependency>
            <groupId>de.ruedigermoeller</groupId>
            <artifactId>fst</artifactId>
            <version>2.52</version>
        </dependency>

        <!--监控本机cpu,内存等信息-->
        <dependency>
            <groupId>org.fusesource</groupId>
            <artifactId>sigar</artifactId>
            <version>1.6.4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <useSystemClassLoader>false</useSystemClassLoader>
                </configuration>
            </plugin>
        </plugins>
    </build>

resource文件夹下新建sigar目录,把库文件加到此目录

Sigar为不同平台提供了不同的库文件.典型的:
windows平台:sigar-x86-winnt.dll
linux平台:libsigar-x86-linux.so或
solaris平台: libsigar-x86-solaris.so或libsigar-sparc-solaris.so或libsigar-sparc64-solaris.so
64位平台:分为至强的libsigar-ia64-linux.so和AMD的libsigar-amd64-linux.so,sigar-amd64-winnt.dll

二,服务器端代码

public class Server {

    private final int port;

    public Server(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
        int port = 8888;
        new Server(port).start();
    }

    public void start() throws Exception {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ch.pipeline()
                                    // 添加编解码. 发送自定义的类型, 而Handler的方法接收的msg参数的实际类型也是相应的自定义类了
                                    .addLast(new TinyDecoder(RequestInfo.class))
                                    .addLast(new TinyEncoder(RequestInfo.class))
                                    .addLast(new ServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture f = b.bind(port).sync();
            System.out.println(Server.class.getName() + " 服务已启动并监控端口" + f.channel().localAddress());
            f.channel().closeFuture().sync();
        } finally {
            //释放 channel 和 块,直到它被关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

public class ServerHandler extends SimpleChannelInboundHandler {
    private static final String SUCCESS_KEY = "auth_success_key";
    private static HashMap<String, String> AUTH_IP_MAP = new HashMap<>();
    private static Set<String> AUTH_IP_SET = new HashSet<>();

    static {
        //检测心跳的地址及验证
        AUTH_IP_MAP.put("192.168.25.1", "1234");
    }

    /**
     * 进行认证
     * 在这里是根据ip和密码进行认证。也可以用账号和密码进行认证获取进行签名等等
     *
     * @param ctx
     * @param msg
     * @return
     */
    private boolean auth(ChannelHandlerContext ctx, Object msg) {
        String[] ret = ((String) msg).split(",");
        String auth = AUTH_IP_MAP.get(ret[0]);
        if (!StringUtil.isNullOrEmpty(auth) && auth.equals(ret[1])) {
            // 认证成功, 返回确认信息
            ctx.writeAndFlush(SUCCESS_KEY);
            //添加到已认证机器上
            AUTH_IP_SET.add(ret[0]);
            return true;
        } else {
            ctx.writeAndFlush("auth failure !").addListener(ChannelFutureListener.CLOSE);
            return false;
        }
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof String) {
            auth(ctx, msg);
        } else if (msg instanceof RequestInfo) {
            RequestInfo info = (RequestInfo) msg;
            if (!AUTH_IP_SET.contains(info.getIp())) {
                System.out.println("尚未认证的机器..." + info.getIp());
                return;
            }
            System.out.println("--------------------" + System.currentTimeMillis() + "------------------------");
            System.out.println("当前主机ip为: " + info.getIp());
            System.out.println("当前主机cpu情况: ");
            HashMap<String, Object> cpu = info.getCpuPercMap();
            System.out.println("总使用率: " + cpu.get("combined"));
            System.out.println("用户使用率: " + cpu.get("user"));
            System.out.println("系统使用率: " + cpu.get("sys"));
            System.out.println("等待率: " + cpu.get("wait"));
            System.out.println("空闲率: " + cpu.get("idle"));

            System.out.println("当前主机memory情况: ");
            HashMap<String, Object> memory = info.getMemoryMap();
            System.out.println("内存总量: " + memory.get("total"));
            System.out.println("当前内存使用量: " + memory.get("used"));
            System.out.println("当前内存剩余量: " + memory.get("free"));
            System.out.println("--------------------------------------------");

            ctx.writeAndFlush("success");
        } else {
            ctx.writeAndFlush("error").addListener(ChannelFutureListener.CLOSE);
        }
    }
}
三。客户端代码

public class Client {
    private final String host;
    private final int port;

    public Client(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
        final String host = "127.0.0.1";
        final int port = 8081;

        new Client(host, port).start();
    }

    public void start() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline()
                                    // 添加编解码. 发送自定义的类型, 而Handler的方法接收的msg参数的实际类型也是相应的自定义类了
                                    .addLast(new TinyDecoder(RequestInfo.class))
                                    .addLast(new TinyEncoder(RequestInfo.class))
                                    .addLast(new ClientHandler());
                        }
                    });

            ChannelFuture f = b.connect().sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }
}
 

public class ClientHandler extends SimpleChannelInboundHandler {
    private static final String SUCCESS_KEY = "auth_success_key";
    /**
     * 开一个线程进行心跳包
     */
    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    /**
     * 定时任务
     */
    private ScheduledFuture<?> heartBeat;
    /**
     * 主动向服务器发送认证信息
     */
    private InetAddress addr;

    /**
     * 通道注册
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    /**
     * 服务器的连接被建立后调用
     * 建立连接后该 channelActive() 方法被调用一次
     *
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws UnknownHostException {
        addr = InetAddress.getLocalHost();
        System.out.println("addr=" + addr);
        String ip = "192.168.25.1";
        String key = "1234";
        //证书
        String auth = ip + "," + key;
        // 发送认证
        ctx.writeAndFlush(auth);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            if (msg instanceof String) {
                String ret = (String) msg;
                if (SUCCESS_KEY.equals(ret)) {
                    // 收到认证 确认信息,设置每隔5秒发送心跳消息
                    this.heartBeat = this.scheduler.scheduleWithFixedDelay(new HeartBeatTask(ctx), 0, 5, TimeUnit.SECONDS);
                    System.out.println("接收到心跳认证消息信息:" + msg);
                }
            } else {
                // 收到心跳包 确认信息
                System.out.println("接收到信息:" + msg);
            }
        } finally {
            // 只读, 需要手动释放引用计数
            ReferenceCountUtil.release(msg);
        }
    }

    /**
     * 捕获异常时调用
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
                                Throwable cause) {
        //记录错误日志并关闭 channel
        cause.printStackTrace();
        ctx.close();
    }


    private class HeartBeatTask implements Runnable {
        private final ChannelHandlerContext ctx;
        private Integer times = 0;

        public HeartBeatTask(final ChannelHandlerContext ctx) {
            this.ctx = ctx;
        }

        @Override
        public void run() {
            try {
                if (times++ > 10) {
                    //取消定时任务
                    closeHeartBeat();
                    return;
                }
                System.out.println("第" + times + "次请求...");
                RequestInfo info = new RequestInfo();
                //ip
                info.setIp(addr.getHostAddress());
                Sigar sigar = SigarUtil.getInstance();
                //cpu prec
                CpuPerc cpuPerc = sigar.getCpuPerc();
                HashMap<String, Object> cpuPercMap = new HashMap<>();
                cpuPercMap.put("combined", cpuPerc.getCombined());
                cpuPercMap.put("user", cpuPerc.getUser());
                cpuPercMap.put("sys", cpuPerc.getSys());
                cpuPercMap.put("wait", cpuPerc.getWait());
                cpuPercMap.put("idle", cpuPerc.getIdle());
                // memory
                Mem mem = sigar.getMem();
                HashMap<String, Object> memoryMap = new HashMap<>();
                memoryMap.put("total", mem.getTotal() / 1024L / 1024L);
                memoryMap.put("used", mem.getUsed() / 1024L / 1024L);
                memoryMap.put("free", mem.getFree() / 1024L / 1024L);
                System.out.println(memoryMap.toString());
                info.setCpuPercMap(cpuPercMap);
                info.setMemoryMap(memoryMap);
                System.out.println(info.toString());
                ctx.writeAndFlush(info);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 出现异常调用
         *
         * @param ctx
         * @param cause
         * @throws Exception
         */
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            // 取消定时发送心跳包的任务
            if (heartBeat != null) {
                heartBeat.cancel(true);
                heartBeat = null;
            }
            ctx.fireExceptionCaught(cause);
        }

        /**
         * 取消定时任务
         */
        public void closeHeartBeat() {
            // 取消定时发送心跳包的任务
            if (heartBeat != null) {
                heartBeat.cancel(true);
                heartBeat = null;
            }
        }
    }

}

四,公共类

public class FstSerializer {
    private static FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();

    /**
     * 反序列化
     *
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        return (T) conf.asObject(data);
    }

    /**
     * 序列化
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> byte[] serialize(T obj) {
        return conf.asByteArray(obj);
    }
}

public class FstSerializer {
    private static FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();

    /**
     * 反序列化
     *
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        return (T) conf.asObject(data);
    }

    /**
     * 序列化
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> byte[] serialize(T obj) {
        return conf.asByteArray(obj);
    }
}

public class TinyDecoder extends ByteToMessageDecoder {
    private Class<?> genericClass;

    public TinyDecoder(Class<?> genericClass) {
        this.genericClass = genericClass;
    }

    /**
     * 解码
     *
     * @param ctx
     * @param in
     * @param out
     */
    @Override
    public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        int size = in.readableBytes();
        byte[] data = new byte[size];
        in.readBytes(data);
        Object obj = FstSerializer.deserialize(data, genericClass);
        out.add(obj);
    }

}
 

@ChannelHandler.Sharable
public class TinyEncoder extends MessageToByteEncoder {
    private Class<?> genericClass;

    public TinyEncoder(Class<?> genericClass) {
        this.genericClass = genericClass;
    }

    /**
     * 编码
     * @param ctx
     * @param in
     * @param out
     * @throws Exception
     */
    @Override
    public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) {
        byte[] data = FstSerializer.serialize(in);
        out.writeBytes(data);
    }

}
 

public class RequestInfo implements Serializable {
    /**
     * 以ip为标识
     */
    private String ip;
    private HashMap<String, Object> cpuPercMap;
    private HashMap<String, Object> memoryMap;
    public String getIp() {
        return ip;
    }
    public void setIp(String ip) {
        this.ip = ip;
    }
    public HashMap<String, Object> getCpuPercMap() {
        return cpuPercMap;
    }
    public void setCpuPercMap(HashMap<String, Object> cpuPercMap) {
        this.cpuPercMap = cpuPercMap;
    }
    public HashMap<String, Object> getMemoryMap() {
        return memoryMap;
    }
    public void setMemoryMap(HashMap<String, Object> memoryMap) {
        this.memoryMap = memoryMap;
    }
    
    
}

public class SigarUtil {
    static {
        // Linux MacOS 分隔符 : Windows 是;
        String osName = System.getProperty("os.name", "generic").toLowerCase();
        String splitSymbol = osName.contains("win") ? ";" : ":";

        // 寻找 classpath 根目录下的 sigar 文件夹
        URL sigarURL = SigarUtil.class.getResource("/sigar");
        if (null == sigarURL) {
            // 找不到抛异常
            throw new MissingResourceException("miss classpath:/sigar folder", SigarUtil.class.getName(), "classpath:/sigar");
        }
        File classPath = new File(sigarURL.getFile());
        String oldLibPath = System.getProperty("java.library.path");
        try {
            // 追加库路径
            String newLibPath = oldLibPath + splitSymbol + classPath.getCanonicalPath();
            System.setProperty("java.library.path", newLibPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Sigar getInstance() {
        return SingleSigar.SIGAR;
    }

    private static class SingleSigar {
        private static final Sigar SIGAR = new Sigar();
    }
}

发布了23 篇原创文章 · 获赞 0 · 访问量 195

猜你喜欢

转载自blog.csdn.net/liuerchong/article/details/105177458