/**
* 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
*
* 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.alibaba.jstorm.utils;
import backtype.storm.utils.ShellUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author JohnFang (xiaojian.fxj@alibaba-inc.com).
*/
public class LinuxResource {
private static final Logger LOG = LoggerFactory.getLogger(LinuxResource.class);
private static final String PROCFS_STAT = "/proc/stat";
private static final Pattern CPU_TIME_FORMAT_ALL =
Pattern.compile("^cpu[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)");
private static final Pattern CPU_TIME_FORMAT = Pattern.compile("^cpu[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)");
private static final String PROCFS_MEMINFO = "/proc/meminfo";
private static volatile String duHome = "./";
public static final long JIFFY_LENGTH_IN_MILLIS;
static {
long jiffiesPerSecond = getJiffies();
JIFFY_LENGTH_IN_MILLIS = jiffiesPerSecond != -1 ? Math.round(1000D / jiffiesPerSecond) : 100;
}
private static long lastCpuTime = -1;
private static long lastIdieTime = -1;
private static float lastcpuUsage = -1;
private static final String PROCFS_NETSTAT = "/proc/net/dev";
public interface ResourceCallback {
Object execute(List<String> lines) throws Exception;
}
public static void setDuHome(String duHome) {
LinuxResource.duHome = duHome;
}
static class ProcResourceParse {
private ResourceCallback callback;
private String file;
public ProcResourceParse(String file, ResourceCallback callback) {
this.callback = callback;
this.file = file;
}
public Object getResource() {
try {
List<String> lines = IOUtils.readLines(new FileInputStream(file));
return callback.execute(lines);
} catch (Exception e) {
LOG.warn("Error reading the stream ", e);
}
return null;
}
}
public static int getProcessNum() {
int sysCpuNum = 0;
try {
sysCpuNum = Runtime.getRuntime().availableProcessors();
} catch (Exception e) {
LOG.info("Failed to get CPU cores .");
}
return sysCpuNum;
}
public static long getJiffies() {
if (!OSInfo.isLinux()) {
return -1;
}
long jiffiesPerSecond = -1;
try {
ShellUtils.ShellCommandExecutor shellExecutorClk =
new ShellUtils.ShellCommandExecutor(new String[]{"getconf", "CLK_TCK"});
shellExecutorClk.execute();
jiffiesPerSecond = Long.parseLong(shellExecutorClk.getOutput().replace("\n", ""));
} catch (Exception e) {
LOG.warn("get jiffies happened error!!!", e);
}
return jiffiesPerSecond;
}
public static synchronized float getTotalCpuUsage() {
if (!OSInfo.isLinux()) {
return 0.0f;
}
ProcResourceParse procResourceParse = new ProcResourceParse(PROCFS_STAT, new ResourceCallback() {
@Override
public Object execute(List<String> lines) throws Exception {
Pair<Long, Long> totalCpu2idle = null;
for (String line : lines) {
long totalCpuTime = 0;
long idleCpuTime = 0;
int size = 0;
Matcher matcher = CPU_TIME_FORMAT_ALL.matcher(line);
if (matcher.find()) {
size = 8;
} else {
matcher = CPU_TIME_FORMAT.matcher(line);
if (matcher.find())
size = 5;
}
for (int i = 1; i < size; i++) {
long value = JStormUtils.parseLong(matcher.group(i));
totalCpuTime += value;
if (i == 4)
idleCpuTime = value;
}
if (size > 0) {
totalCpu2idle = new Pair<>();
totalCpu2idle.setFirst(totalCpuTime);
totalCpu2idle.setSecond(idleCpuTime);
break;
}
}
return totalCpu2idle;
}
});
Object result = procResourceParse.getResource();
if (result == null) {
LOG.warn("getTotalCpuUsage failed");
return 0.0f;
}
Pair<Long, Long> totalCpu2idle = (Pair<Long, Long>) result;
if (lastCpuTime == -1 ||
lastCpuTime > totalCpu2idle.getFirst()) {
lastCpuTime = totalCpu2idle.getFirst();
lastIdieTime = totalCpu2idle.getSecond();
return lastcpuUsage;//return -1 first time
}
if (totalCpu2idle.getFirst() > lastCpuTime + 1) {
float deltaCpu = totalCpu2idle.getFirst() - lastCpuTime;
float deltaIdle = totalCpu2idle.getSecond() - lastIdieTime;
lastcpuUsage = (1 - deltaIdle / deltaCpu) * 100f;
lastCpuTime = totalCpu2idle.getFirst();
lastIdieTime = totalCpu2idle.getSecond();
}
return lastcpuUsage;
}
public static double getTotalMemUsage() {
if (!OSInfo.isLinux()) {
return 0.0;
}
try {
Map<String, String> memInfo = new HashMap<>();
List<String> lines = IOUtils.readLines(new FileInputStream(PROCFS_MEMINFO));
for (String line : lines) {
String key = line.split("\\s+")[0];
String value = line.split("\\s+")[1];
memInfo.put(key, value);
}
String total = memInfo.get("MemTotal:");
String free = memInfo.get("MemFree:");
String buffer = memInfo.get("Buffers:");
String cache = memInfo.get("Cached:");
return 1 - (Double.valueOf(free) + Double.valueOf(buffer) + Double.valueOf(cache)) / Double.valueOf(total);
} catch (Exception ignored) {
LOG.warn("failed to get total memory usage.", ignored);
}
return 0.0;
}
public static Long getFreePhysicalMem() {
if (!OSInfo.isLinux()) {
return 0L;
}
try {
List<String> lines = IOUtils.readLines(new FileInputStream(PROCFS_MEMINFO));
String free = lines.get(1).split("\\s+")[1];
return Long.valueOf(free);
} catch (Exception ignored) {
LOG.warn("failed to get total free memory.");
}
return 0L;
}
/**
* calculate the disk usage at current filesystem
*
* @return disk usage, from 0.0 ~ 1.0
*/
public static Double getDiskUsage() {
if (!OSInfo.isLinux() && !OSInfo.isMac()) {
return 0.0;
}
try {
String output = SystemOperation.exec("df -h " + duHome);
if (output != null) {
String[] lines = output.split("[\\r\\n]+");
if (lines.length >= 2) {
String[] parts = lines[1].split("\\s+");
if (parts.length >= 5) {
String pct = parts[4];
if (pct.endsWith("%")) {
return Integer.valueOf(pct.substring(0, pct.length() - 1)) / 100.0;
}
}
}
}
} catch (Exception e) {
LOG.warn("failed to get disk usage.");
}
return 0.0;
}
public static Pair<Long, Long> getNetRevSendCount() {
Pair<Long, Long> pair = new Pair<>(0l, 0l);
if (!OSInfo.isLinux()) {
return pair;
}
ProcResourceParse procResourceParse = new ProcResourceParse(PROCFS_NETSTAT, new ResourceCallback() {
@Override
public Object execute(List<String> lines) throws Exception {
Pair<Long, Long> pair = new Pair<>(0l, 0l);
int receiveIndex = -1;
int sendIndex = -1;
for (String line : lines) {
String[] splitString = line.split("\\s+|\\||:");
List<String> strings = new ArrayList<>();
for (String str : splitString) {
if (str.trim().length() > 0) {
strings.add(str);
}
}
if (strings.size() > 0 && strings.get(0).equals("face")) {
for (int i = 0; i < strings.size(); i++) {
if (strings.get(i).equals("bytes")) {
if (receiveIndex == -1) {
receiveIndex = i;
} else {
sendIndex = i;
}
}
}
break;
}
}
if (receiveIndex != -1 && sendIndex != -1) {
for (String line : lines) {
String[] splitString = line.split("\\s+|\\||:");
List<String> strings = new ArrayList<>();
for (String str : splitString) {
if (str.trim().length() > 0) {
strings.add(str);
}
}
if (strings.size() > 0 && strings.get(0).startsWith("eth")) {
long rec = JStormUtils.parseLong(strings.get(receiveIndex));
long send = JStormUtils.parseLong(strings.get(sendIndex));
pair.setFirst(pair.getFirst() + rec);
pair.setSecond(pair.getSecond() + send);
}
}
} else {
LOG.warn("receiveIndex {}, sendIndex {}", receiveIndex, sendIndex);
}
return pair;
}
});
Object result = procResourceParse.getResource();
if (result != null)
pair = (Pair<Long, Long>) result;
return pair;
}
}