/** * Copyright 2016 benjobs * <p> * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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 org.opencron.agent; import com.alibaba.fastjson.JSON; import com.corundumstudio.socketio.Configuration; import com.corundumstudio.socketio.SocketIOClient; import com.corundumstudio.socketio.SocketIOServer; import com.corundumstudio.socketio.listener.ConnectListener; import com.corundumstudio.socketio.listener.DisconnectListener; import org.opencron.common.job.Monitor; import org.opencron.common.utils.*; import org.slf4j.Logger; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.Format; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.opencron.common.utils.CommonUtils.*; import static org.opencron.common.utils.CommandUtils.executeShell; /** * Created by benjobs on 16/4/7. */ public class AgentMonitor { private Logger logger = LoggerFactory.getLogger(AgentMonitor.class); private Format format = new DecimalFormat("##0.00"); private boolean stop = false; private Map<UUID, SocketIOClient> clients = new HashMap<UUID, SocketIOClient>(0); public void start(final int port) throws Exception { final Configuration configuration = new Configuration(); configuration.setPort(port); final SocketIOServer server = new SocketIOServer(configuration); server.addConnectListener(new ConnectListener() {//添加客户端连接监听器 @Override public void onConnect(final SocketIOClient client) { UUID sessionId = client.getSessionId(); logger.info("[opencron]:monitor connected:SessionId @ {},port @ {}", sessionId, port); clients.put(sessionId, client); /** * 断开连接或者获取数据错误,不在推送数据... */ while (clients.get(sessionId) != null) { Monitor monitor = monitor(); if (monitor == null) { stop = true; clients.remove(sessionId); server.stop(); break; } client.sendEvent("monitor", monitor); try { TimeUnit.MICROSECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); server.addDisconnectListener(new DisconnectListener() { @Override public void onDisconnect(SocketIOClient client) { /** * 一旦客户端断开连接,立即将该连接实例移除... */ clients.remove(client.getSessionId()); logger.info("[opencron]:monitor disconnect:SessionId @ {},port @ {} ", client.getSessionId(), port); if (clients.isEmpty()) { stop = true; logger.info("[opencron]:monitor closed:SessionId @ {},port @ {} ", client.getSessionId(), port); server.stop(); } } }); server.start(); stop = false; logger.debug("[opencron] server started @ {}", port); } public Monitor monitor() { try { Monitor monitor = new Monitor(); String monitorString = executeShell(Globals.OPENCRON_MONITOR_SHELL); Monitor.Info info = JSON.parseObject(monitorString, Monitor.Info.class); monitor.setTop(getTop(info.getTop())); //config... monitor.setConfig(info.getConf()); //cpu monitor.setCpuData(getCpuData(info)); //内存 monitor.setMemUsage(setMemUsage(info)); //磁盘 monitor.setDiskUsage(getDiskUsage(info)); //swap monitor.setSwap(getSwap(info)); //load(负载) monitor.setLoad(getLoad(info)); //time monitor.setTime(DateUtils.parseStringFromDate(new Date(), "HH:mm:ss")); return monitor; } catch (Exception e) { e.printStackTrace(); } return null; } private String getTop(String str) throws Exception { Map<Integer, String> index = new HashMap<Integer, String>(0); List<Field> fields = Arrays.asList(Monitor.Top.class.getDeclaredFields()); List<String> topList = new ArrayList<String>(0); Scanner scan = new Scanner(str); boolean isFirst = true; while (scan.hasNextLine()) { String text = scan.nextLine().trim(); String data[] = text.split("\\s+"); if (isFirst) { for (int i = 0; i < data.length; i++) { for (Field f : fields) { if (f.getName().equalsIgnoreCase(data[i].replaceAll("%|\\+", ""))) { index.put(i, f.getName()); } } } isFirst = false; } else { Monitor.Top top = new Monitor.Top(); for (Map.Entry<Integer, String> entry : index.entrySet()) { ReflectUitls.setter(Monitor.Top.class, entry.getValue()).invoke(top, data[entry.getKey()]); } topList.add(JSON.toJSONString(top)); } } scan.close(); return JSON.toJSONString(topList); } public String getCpuData(Monitor.Info info) { //cpu usage report.. Long sysIdle = toLong(new BigDecimal(info.getCpu().getId2()).toPlainString()) - toLong(new BigDecimal(info.getCpu().getId1()).toPlainString()); Long total = toLong(new BigDecimal(info.getCpu().getTotal2()).toPlainString()) - toLong(new BigDecimal(info.getCpu().getTotal1()).toPlainString()); Map<String, String> cpuData = new HashMap<String, String>(0); if(sysIdle.equals(0L)){ if(total.equals(0L)){ cpuData.put("usage", "0.00"); }else { cpuData.put("usage", "100.00"); } }else { float sysUsage = (sysIdle.floatValue() / total.floatValue()) * 100; float sysRate = 100 - sysUsage; if (sysRate == 0.0f) { cpuData.put("usage", "0.00"); } else { cpuData.put("usage", new DecimalFormat("##0.00").format(sysRate)); } } //cpu detail... String cpuDetail[] = info.getCpu().getDetail().split(","); for (String detail : cpuDetail) { String key = null, val = null; Matcher valMatcher = Pattern.compile("^\\d+(\\.\\d+)?").matcher(detail); if (valMatcher.find()) { val = valMatcher.group(); } Matcher keyMatcher = Pattern.compile("[a-zA-Z]+$").matcher(detail); if (keyMatcher.find()) { key = keyMatcher.group(); } cpuData.put(key, val); } return JSON.toJSONString(cpuData); } private String getDiskUsage(Monitor.Info info) throws Exception { /** * get info... */ Map<String, String> map = new HashMap<String, String>(0); Scanner scanner = new Scanner(info.getDisk()); List<String> tmpArray = new ArrayList<String>(0); int usedIndex = 2, availIndex = 3, mountedIndex = 5; /** * title index.... */ String title = scanner.nextLine(); List<String> strArray = Arrays.asList(title.split("\\s+")); int len = strArray.size(); /** * data..... * * 注意:当Filesystem的值太长,导致本来一行的数据换行问题. * */ while (scanner.hasNextLine()) { String content = scanner.nextLine(); strArray = Arrays.asList(content.split("\\s+")); if (strArray.size() == len) { map.put(strArray.get(mountedIndex), strArray.get(usedIndex) + "," + strArray.get(availIndex)); } else if (strArray.size() < len) {//某个字段对应的值太长,导致本来一行的数据换行问题. if (tmpArray.isEmpty()) { tmpArray = new ArrayList<String>(strArray); } else { tmpArray.addAll(strArray); if (tmpArray.size() == len) { /** * 合并后的一行数据 */ map.put(tmpArray.get(mountedIndex), tmpArray.get(usedIndex) + "," + tmpArray.get(availIndex)); tmpArray = Collections.emptyList(); } } } } scanner.close(); //set detail.... List<Map<String, String>> disks = new ArrayList<Map<String, String>>(); Double usedTotal = 0D; Double freeTotal = 0D; for (Map.Entry<String, String> entry : map.entrySet()) { Map<String, String> disk = new HashMap<String, String>(); try { Double used = generateDiskSpace(entry.getValue().split(",")[0]); Double free = generateDiskSpace(entry.getValue().split(",")[1]); usedTotal += used; freeTotal += free; disk.put("disk", entry.getKey()); disk.put("used", format.format(used)); disk.put("free", format.format(free)); disks.add(disk); }catch (NumberFormatException e) { continue; } } Map<String, String> disk = new HashMap<String, String>(); disk.put("disk", "usage"); disk.put("used", format.format(usedTotal)); disk.put("free", format.format(freeTotal)); disks.add(disk); return JSON.toJSONString(disks); } public String getNetwork(Monitor.Info info) { Map<String, Float> newWork = new HashMap<String, Float>(); float read = 0; float write = 0; for (Monitor.Net net : info.getNet()) { read += net.getRead(); write += net.getWrite(); } newWork.put("read", read); newWork.put("write", write); return JSON.toJSONString(newWork); } public String getSwap(Monitor.Info info) { if (info.getSwap().getTotal() == 0.0f) { return "0.00"; } if (info.getSwap().getTotal() - info.getSwap().getFree() == 0.0f) { return "100.00"; } return format.format(((info.getSwap().getTotal() - info.getSwap().getFree()) / info.getSwap().getTotal()) * 100); } public List<String> getLoad(Monitor.Info info) { return Arrays.asList(info.getLoad().split(",")); } private String setMemUsage(Monitor.Info info) { if (info.getMem().getTotal() == 0.00 || info.getMem().getUsed() == 0) { return "0.00"; } return format.format((info.getMem().getUsed() / info.getMem().getTotal()) * 100); } public List<String> getIostat() throws Exception { /** * rrqm/s: 每秒进行 merge 的读操作数目。即 rmerge/s wrqm/s: 每秒进行 merge 的写操作数目。即 wmerge/s r/s: 每秒完成的读 I/O 设备次数。即 rio/s w/s: 每秒完成的写 I/O 设备次数。即 wio/s rkB/s: 每秒读K字节数。是 rsect/s 的一半,因为每扇区大小为512字节。 wkB/s: 每秒写K字节数。是 wsect/s 的一半。 avgrq-sz: 平均每次设备I/O操作的数据大小 (扇区)。 avgqu-sz: 平均I/O队列长度。 await: 平均每次设备I/O操作的等待时间 (毫秒)。 svctm: 平均每次设备I/O操作的服务时间 (毫秒)。 %util: 一秒中有百分之多少的时间用于 I/O 操作,即被io消耗的cpu百分比 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm util sda 0.31 35.13 0.53 14.80 8.93 191.44 26.16 0.03 2.09 0.60 0.92 dm-0 0.00 0.00 0.07 2.58 0.93 10.33 8.47 0.01 1.91 0.39 0.10 dm-1 0.00 0.00 0.27 0.46 1.07 1.85 8.00 0.02 21.16 0.04 0.00 dm-2 0.00 0.00 0.50 44.82 6.93 179.27 8.22 0.57 12.63 0.18 0.83 * **/ Map<Integer, String> index = new HashMap<Integer, String>(0); List<Field> fields = Arrays.asList(Monitor.Iostat.class.getDeclaredFields()); List<String> ioList = new ArrayList<String>(0); String result = CommandUtils.executeShell(Globals.OPENCRON_MONITOR_SHELL, "io"); Scanner scan = new Scanner(result); boolean isFirst = true; while (scan.hasNextLine()) { String text = scan.nextLine(); String data[] = text.split("\\s+"); if (isFirst) { for (int i = 0; i < data.length; i++) { for (Field f : fields) { if (f.getName().equalsIgnoreCase(data[i].replaceAll("/s|:|-.+", ""))) { index.put(i, f.getName()); } } } isFirst = false; } else { Monitor.Iostat iostat = new Monitor.Iostat(); for (Map.Entry<Integer, String> entry : index.entrySet()) { Method setMethod = ReflectUitls.setter(Monitor.Iostat.class, entry.getValue()); setMethod.invoke(iostat, data[entry.getKey()]); } ioList.add(JSON.toJSONString(iostat)); } } scan.close(); return ioList; } private Double generateDiskSpace(String value) { String fix = value.substring(value.length() - 1); String space = value.substring(0, value.length() - 1); if (fix.equalsIgnoreCase("D")) { return Double.parseDouble(space) * Math.pow(1024, 8); } if (fix.equalsIgnoreCase("N")) { return Double.parseDouble(space) * Math.pow(1024, 7); } if (fix.equalsIgnoreCase("B")) { return Double.parseDouble(space) * Math.pow(1024, 6); } if (fix.equalsIgnoreCase("Y")) { return Double.parseDouble(space) * Math.pow(1024, 5); } if (fix.equalsIgnoreCase("Z")) { return Double.parseDouble(space) * Math.pow(1024, 4); } if (fix.equalsIgnoreCase("E")) { return Double.parseDouble(space) * Math.pow(1024, 3); } if (fix.equalsIgnoreCase("P")) { return Double.parseDouble(space) * Math.pow(1024, 2); } if (fix.equalsIgnoreCase("T")) { return Double.parseDouble(space) * Math.pow(1024, 1); } if (fix.equalsIgnoreCase("G")) { return Double.parseDouble(space); } if (fix.equalsIgnoreCase("M")) { return Double.parseDouble(space) / 1024; } return 0D; } public boolean stoped() { return stop; } }