一,加入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();
}
}