SpringBoot监控服务器内存信息简介
/**
* 定时任务监控服务器内存信息
*/
@Component
public class OsMonitorScheduleTask implements Runnable {
@Resource
private OsMonitorMapper osMonitorMapper;
@Resource
private SystemUserMapper systemUserMapper;
private final FeedbackData feedbackData;
private static final Logger log = LoggerFactory.getLogger(ValidCheckTask.class);
@Autowired
public OsMonitorScheduleTask(FeedbackData feedbackData) {
this.feedbackData = feedbackData;
}
@Override
public void run() {
try {
doBusiness();
} catch (Exception e) {
log.error("It has an error when get monitor info. message:{}", e.getMessage());
}
}
/**
* 具体业务处理
*/
private void doBusiness() {
// 重新加载配置
ConfigProperties.loadConfig(FileUtils.getWebRootPath());
int WARNING_THRESHOLD = Integer.parseInt(ConfigProperties.getKey(ConfigList.BASIC, "ALARM_THRESHOLD"));
int sentType = Integer.parseInt(ConfigProperties.getKey(ConfigList.BASIC, "ALARM_SEND_TYPE"));
OsMonitorInfoBean monitorInfo = OsMonitorUtils.GetMonitorInfo();
monitorInfo.setThreshold(WARNING_THRESHOLD);
monitorInfo.setRecordTime(new java.util.Date());
double cpuRatio = Double.parseDouble(monitorInfo.getCpuRatio());
if (cpuRatio < WARNING_THRESHOLD) {
monitorInfo.setWarning(0);
osMonitorMapper.osMonitorLogInsert(monitorInfo);
return;
}
monitorInfo.setWarning(1);
OsMonitorInfoBean lastAlarm = osMonitorMapper.getLatestAlarmedMonitorInfo();
long alarmInterval = Long.parseLong(ConfigProperties.getKey(ConfigList.BASIC, "ALARM_INTERVAL_MINUTE"));
if (lastAlarm != null && ((new java.util.Date().getTime() - lastAlarm.getRecordTime().getTime()) <= (alarmInterval - 2) * 60 * 1000)) {
monitorInfo.setAlarmType(0);
osMonitorMapper.osMonitorLogInsert(monitorInfo);
return; // 存在上一次报警但不超过 alarmInterval 分钟, 不报警
}
List<SystemUserBean> adminUsers = systemUserMapper.getSystemUserByName("superUser");
// 短信通知管理员
if (sentType == 0) {
monitorInfo.setAlarmType(0);
osMonitorMapper.osMonitorLogInsert(monitorInfo);
log.error("突破报警阈值,但不发送报警信息");
} else if (sentType == 1) { // 发短信
monitorInfo.setAlarmType(1);
monitorInfo.setAlarmAddr(adminUsers.get(0).getPhone());
osMonitorMapper.osMonitorLogInsert(monitorInfo);
SendSmsMessage(WARNING_THRESHOLD, cpuRatio, adminUsers);
} else if (sentType == 2) { // 发邮箱
monitorInfo.setAlarmType(2);
monitorInfo.setAlarmAddr(adminUsers.get(0).getEmail());
osMonitorMapper.osMonitorLogInsert(monitorInfo);
SendMailMessage(WARNING_THRESHOLD, cpuRatio, adminUsers);
} else if (sentType == 3) { // 短信、邮箱同时发
monitorInfo.setAlarmType(3);
monitorInfo.setAlarmAddr(adminUsers.get(0).getPhone() + ";" + adminUsers.get(0).getEmail());
osMonitorMapper.osMonitorLogInsert(monitorInfo);
SendSmsMessage(WARNING_THRESHOLD, cpuRatio, adminUsers);
SendMailMessage(WARNING_THRESHOLD, cpuRatio, adminUsers);
}
}
// 发送短信
private void SendSmsMessage(int WARNING_THRESHOLD, double cpuRatio, List<SystemUserBean> adminUsers) {
if (adminUsers == null || adminUsers.size() == 0
|| adminUsers.get(0).getPhone() == null
|| adminUsers.get(0).getPhone().equals("")) {
return;
}
FeedBack.addFeedbackMessage(message);
}
// 发送邮箱
private void SendMailMessage(int WARNING_THRESHOLD, double cpuRatio, List<SystemUserBean> adminUsers) {
if (adminUsers == null || adminUsers.size() == 0
|| adminUsers.get(0).getEmail() == null
|| adminUsers.get(0).getEmail().equals("")) {
return;
}
FeedBackMessageBean message = new FeedBackMessageBean();
String mailContact = adminUsers.get(0).getEmail();
message.setContact(mailContact);
message.setMsgtype(FeedbackDataType.MAIL_TYPE);
message.setSubject("后台CPU报警");
message.setContent(String.format("后台CPU使用率已达 %.2f%%,超过阈值 %2d%%,请关注。", cpuRatio, WARNING_THRESHOLD));
feedbackData.addFeedbackMessage(message);
}
}
FeedBack
@Component
public class FeedbackData {
private static ConcurrentLinkedQueue<FeedBackMessageBean> queue = new ConcurrentLinkedQueue<FeedBackMessageBean>();
private final RBlockingQueue<FeedBackMessageBean> rQueue;
@Autowired
public FeedbackData(RBlockingQueueService rBlockingQueueService) {
rQueue = rBlockingQueueService.getBlockingQueue("FeedBackMessage");
}
/**
* 添加信息到队列等待发送
*
* @param messageBean FeedBackMessageBean
*/
public void addFeedbackMessage(FeedBackMessageBean messageBean) {
if (messageBean != null) {
//queue.add(messageBean);
rQueue.add(messageBean);
}
}
/**
* 获取队列任务信息
*
* @return FeedBackMessageBean
*/
public FeedBackMessageBean getFeedbackMessage() {
if (!rQueue.isEmpty()) {
return rQueue.poll();
}
return null
}
}
SendFeedBack
public class SendFeedbackTask implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(SendFeedbackTask.class);
private final FeedbackData feedbackData;
/**
* 线程是否存活
*/
private boolean alive = false;
/**
* 消息处理线程
*/
private Thread t = null;
@Autowired
public SendFeedbackTask(FeedbackData feedbackData) {
this.feedbackData = feedbackData;
}
/**
* 关闭线程
*/
public void stop() {
alive = false;
}
/**
* 启动线程
*/
public void start() {
alive = true;
t = new Thread(this);
t.setName(this.getClass().getSimpleName());
t.start();
}
@Override
public void run() {
do {
try {
if (!doBusiness()) {
try {
Thread.sleep(100);
} catch (Exception e) {
logger.error("It has an error when thread sleep.");
}
}
} catch (Exception e) {
try {
Thread.sleep(100);
} catch (Exception ee) {
logger.error("It has an error when thread sleep.");
}
logger.debug("It has an error when receive message. {}", e.getLocalizedMessage());
}
} while (alive);
}
private boolean doBusiness() {
FeedBackMessageBean message = feedbackData.getFeedbackMessage();
if (message != null) {
if (message.getMsgtype() == FeedbackDataType.SMS_TYPE) {
SmsServerBean smsBean = (SmsServerBean) ConfigData.getConfigData(FieldConstants.SMS_SERVER_TYPE);
if (smsBean == null || StringUtils.isEmpty(smsBean.getAccessKeyID())
|| StringUtils.isEmpty(smsBean.getAccessKeySecret())
|| StringUtils.isEmpty(smsBean.getSignName())
|| StringUtils.isEmpty(smsBean.getFeedBackTemplateCode())
|| StringUtils.isEmpty(smsBean.getAuthTemplateCode())) {
logger.error("It has an error when get the sms template.");
return false;
}
if (!StringUtils.isEmpty(message.getContent()) && !StringUtils.isEmpty(message.getContact())) {
if (message.getSmstype() == FeedbackDataType.SMS_QUESTION_TYPE) {
String content = "{'question':'" + message.getContent() + "'}";
SMSUtils.sendSMSByAliyun(smsBean, message.getContact(), smsBean.getFeedBackTemplateCode(),
content);
}
if (message.getSmstype() == FeedbackDataType.SMS_AUTH_TYPE) {
String content = "{'reason':'" + message.getContent() + "'}";
SMSUtils.sendSMSByAliyun(smsBean, message.getContact(), smsBean.getAuthTemplateCode(), content);
}
if (!StringUtils.isEmpty(message.getContent()) && !StringUtils.isEmpty(message.getContact())) {
if (message.getSmstype() == FeedbackDataType.SMS_ALARM_TYPE) {
String content = "{'param':'" + message.getContent() + "'}";
SMSUtils.sendSMSByAliyun(smsBean, message.getContact(), smsBean.getAlarmTemplateCode(), content);
}
}
return true;
}
}
if (message.getMsgtype() == FeedbackDataType.MAIL_TYPE) {
MailServerBean mailServerBean = (MailServerBean) ConfigData
.getConfigData(FieldConstants.MAIL_SERVER_TYPE);
if (mailServerBean == null || StringUtils.isEmpty(mailServerBean.getHost())
|| StringUtils.isEmpty(mailServerBean.getPort())
|| StringUtils.isEmpty(mailServerBean.getMail())
|| StringUtils.isEmpty(mailServerBean.getPassword())) {
logger.error("It has an error when get the mail template.");
return false;
}
MailMessage mailInfo = new MailMessage();
mailInfo.setMailServerHost(mailServerBean.getHost());
mailInfo.setMailServerPort(mailServerBean.getPort());
mailInfo.setValidate(true);
mailInfo.setUserName(mailServerBean.getMail());
mailInfo.setPassword(mailServerBean.getPassword());
mailInfo.setFromAddress(mailServerBean.getFromaddress());
String mailBody = message.getContent();
boolean useHtmlTemplate = message.getUseHtmlTemplate();
if (!useHtmlTemplate) { // 没有使用模板时需要替换
mailBody = mailBody.replaceAll(" ", " ");
mailBody = mailBody.replaceAll("\n", "<br/>");
}
mailInfo.setToAddress(message.getContact());
mailInfo.setSubject(message.getSubject());
mailInfo.setContent(mailBody);
if (MailSender.sendHtmlMail(mailInfo)) {
logger.info("success to send mail. receiver={}", message.getContact());
} else {
logger.info("failed to send mail. receiver={}", message.getContact());
}
}
}
return false;
}
}
邮件内容主题
// 发送邮件的服务器的IP和端口
private String mailServerHost;
private String mailServerPort = "25";
// 邮件发送者的地址
private String fromAddress;
// 邮件接收者的地址
private String toAddress;
// 登陆邮件发送服务器的用户名和密码
private String userName;
private String password;
// 是否需要身份验证
private boolean validate = false;
// 邮件主题
private String subject;
// 邮件的文本内容
private String content;
// 邮件附件的文件名
private String[] attachFileNames;
// 重传次数 最多3次
private Integer reSentTimes = 0;
/**
* 获得邮件会话属性
*/
public Properties getProperties() {
Properties p = new Properties();
MailSSLSocketFactory sf = null;
try {
sf = new MailSSLSocketFactory();
} catch (GeneralSecurityException e1) {
e1.printStackTrace();
log.error("getProperties get an exception:" + e1.getLocalizedMessage());
return p;
}
sf.setTrustAllHosts(true);
p.put("mail.smtp.host", this.mailServerHost);
p.put("mail.smtp.port", this.mailServerPort);
if ("ON".equalsIgnoreCase(ConfigProperties.getKey(ConfigList.BASIC, "SMTP_TLS"))) {
p.put("mail.smtp.starttls.enable", "true");
} else {
p.put("mail.smtp.starttls.enable", "false");
}
p.put("mail.smtp.connectiontimeout", "30000");
p.put("mail.smtp.timeout", "30000");
p.put("mail.smtp.auth", validate ? "true" : "false");
p.put("mail.pop3.socketFactory", sf);
p.put("mail.pop3.socketFactory.fallback", "false");
p.put("mail.pop3.port", "995");
p.put("mail.pop3.socketFactory.port", "995");
p.put("mail.pop3.disabletop", "true");
p.put("mail.pop3.ssl.enable", "true");
p.put("mail.pop3.useStartTLS", "true");
return p;
}
系统内存信息
/**
* 操作系统名
*/
private String osName;
/**
* cpu使用率.
*/
private String cpuRatio;
/**
* 系统总内存
*/
private String osTotalMemory;
/**
* 系统剩余内存.
*/
private String osFreeMemory;
/**
* 最大可获取内存.
*/
private String maxReachableMemory;
/**
* 已分配的内存的剩余量
*/
private String allocatedFreeMemory;
/**
* 已分配的内存的使用量
*/
private String allocatedUsedMemory;
/**
* 最大可使用内存
*/
private String maxUsableMemory;
/**
* 线程总数
*/
private Integer appThread;
/**
* 记录时间
*/
private Date recordTime;
/**
* 报警阈值
*/
private Integer threshold;
/**
* 是否报警
*/
private Integer warning;
/**
* 报警类型 0:不报警,1:短信,2:邮箱,3:短信+邮箱
*/
private Integer alarmType;
/**
* 报警地址(短信/邮箱)
*/
private String alarmAddr;
获取系统信息
/**
*
* 获取系统信息
*/
public class getSystemInfoUtils{
/**
* 获取 OS Name
*/
private static String GetOsName() {
return System.getProperty("os.name");
}
/**
* 获取 JVM 参数
*/
private static void GetJvmMemoryInfo(OsMonitorInfoBean infoBean) {
double mb = 1024 * 1024 * 1.0;
// jvm
double totalMemory = Runtime.getRuntime().totalMemory() / mb;
double freeMemory = Runtime.getRuntime().freeMemory() / mb;
double maxMemory = Runtime.getRuntime().maxMemory() / mb;
DecimalFormat df = new DecimalFormat("#.##M");
// MonitorInfo
infoBean.setMaxReachableMemory(df.format(maxMemory - totalMemory));
infoBean.setAllocatedFreeMemory(df.format(freeMemory));
infoBean.setAllocatedUsedMemory(df.format(totalMemory - freeMemory));
infoBean.setMaxUsableMemory(df.format(maxMemory));
}
/**
* 得到类所在磁盘
*/
public static String getClassLocaledDisk() {
Class<OsMonitorUtils> thisClass = OsMonitorUtils.class;
try {
String strClassName = thisClass.getName();
String strPackageName = "";
if (thisClass.getPackage() != null) {
strPackageName = thisClass.getPackage().getName();
}
String strClassFileName = "";
if (!"".equals(strPackageName)) {
strClassFileName = strClassName.substring(strPackageName.length() + 1);
} else {
strClassFileName = strClassName;
}
URL url = null;
url = thisClass.getResource(strClassFileName + ".class");
String strURL = url.toString();
strURL = strURL.substring(strURL.indexOf('/') + 1);
//返回当前类的路径,并且处理路径中的空格,因为在路径中出现的空格如果不处理的话,
//在访问时就会从空格处断开,那么也就取不到完整的信息了,这个问题在web开发中尤其要注意
return strURL.substring(0, strURL.indexOf('/'));
} catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
}
/**
* 获取App运行中的线程数
*/
private static int GetAppProcessingNum() {
try {
//获取所有运行中的线程
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
return map.size();
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public static final SystemInfo si = new SystemInfo();
public static final CentralProcessor cpu = si.getHardware().getProcessor();
public static final Object oshiLock = new Object();
static {
cpu.getSystemCpuLoadTicks(); // 预加载一下,第一次调用很慢
}
/**
* 获取 Oshi CPU 综合使用率
*/
public static String GetOshiCpuRadio(CentralProcessor processor) {
long[] oldTicks = processor.getSystemCpuLoadTicks();
// 睡眠1s
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
double totalCpu = processor.getSystemCpuLoadBetweenTicks(oldTicks);
DecimalFormat df = new DecimalFormat("#.##");
return df.format(totalCpu * 100D);
}
// ***************使用第三方框架OSHI获取系统信息************ //
/**
* 获取 Oshi 内存
*/
public static GlobalMemory GetOshiMemory(SystemInfo si) {
return si.getHardware().getMemory();
}
/**
* 获取 Oshi 总内存
*/
public static String GetOshiTotalMemory(GlobalMemory memory) {
Double total = (double) memory.getTotal() / 1024 / 1024;
DecimalFormat df = new DecimalFormat("#.##M");
return df.format(total);
}
/**
* 获取 Oshi 可用内存
*/
public static String GetOshiFreeMemory(GlobalMemory memory) {
Double free = (double) memory.getAvailable() / 1024 / 1024;
DecimalFormat df = new DecimalFormat("#.##M");
return df.format(free);
}
/**
* 获取 Oshi 内存占比
*/
public static String GetOshiMemoryPerc(SystemInfo si) {
GlobalMemory memory = si.getHardware().getMemory();
double total = (double) memory.getTotal();
double used = total - (double) memory.getAvailable();
double perc = used / total;
DecimalFormat df = new DecimalFormat("#0");
return df.format(perc * 100D);
}
/**
* 获取 Oshi 磁盘使用占比
*/
public static String GetOshiDiskPerc(SystemInfo si) {
FileSystem fileSystem = si.getOperatingSystem().getFileSystem();
List<OSFileStore> fileStores = fileSystem.getFileStores();
double usable = 0;
double total = 0;
double perc = 0;
int diskCount = 0;
for (OSFileStore store : fileStores) {
logger.info("fileStore name: {}", store.getName());
usable += (double) store.getUsableSpace();
total += (double) store.getTotalSpace();
perc += (total - usable) / total * 100D;
diskCount++;
}
DecimalFormat df = new DecimalFormat("#0");
if (diskCount != 0) {
return df.format(perc / diskCount);
} else {
return df.format(0);
}
}
获取监控信息 ///
///
// 获取监控信息
public static OsMonitorInfoBean GetMonitorInfo() {
// return GetSigarMonitorInfo();
return GetOshiMonitorInfo();
}
public static OsMonitorInfoBean GetOshiMonitorInfo() {
synchronized (oshiLock) {
OsMonitorInfoBean infoBean = new OsMonitorInfoBean();
infoBean.setOsName(GetOsName());
infoBean.setCpuRatio(GetOshiCpuRadio(cpu));
GlobalMemory memory = GetOshiMemory(si);
infoBean.setOsTotalMemory(GetOshiTotalMemory(memory));
infoBean.setOsFreeMemory(GetOshiFreeMemory(memory));
GetJvmMemoryInfo(infoBean);
infoBean.setAppThread(GetAppProcessingNum());
return infoBean;
}
}
/**
* CPU的用户使用量、系统使用剩余量、总的剩余量、总的使用占用量等(单位:100%)
*/
public static String getCpuPerc() {
return getOshiCpuPerc();
}
/**
* 内存资源信息
*/
public static String getPhysicalMemory() {
return getOshiMemoryPerc();
}
/**
* 资源信息(主要是硬盘) 平均占比
* 取硬盘已有的分区及其详细信息
* 通过 sigar.getFileSystemList()来获得FileSystem列表对象,然后对其进行编历
*/
public static String getFileSystemInfo() {
return getOshiFileSystemInfo();
}
private static final SystemInfo si = OsMonitorUtils.si;
private static final CentralProcessor cpu = OsMonitorUtils.cpu;
private static final Object oshiLock = OsMonitorUtils.oshiLock;
/**
* CPU使用占比
*/
public static String getOshiCpuPerc() {
synchronized (oshiLock) {
return OsMonitorUtils.GetOshiCpuRadio(cpu);
}
}
/**
* 内存使用占比
*/
public static String getOshiMemoryPerc() {
synchronized (oshiLock) {
return OsMonitorUtils.GetOshiMemoryPerc(si);
}
}
/**
* 磁盘使用占比
*/
public static String getOshiFileSystemInfo() {
synchronized (oshiLock) {
return OsMonitorUtils.GetOshiDiskPerc(si);
}
}
// 获取操作平台信息
public static String getOsPrefix() {
String arch = System.getProperty("os.arch").toLowerCase();
final String name = System.getProperty("os.name");
String osPrefix;
switch (Platform.getOSType()) {
case Platform.WINDOWS: {
if ("i386".equals(arch))
arch = "x86";
osPrefix = "win32-" + arch;
}
break;
case Platform.LINUX: {
if ("x86".equals(arch)) {
arch = "i386";
} else if ("x86_64".equals(arch)) {
arch = "amd64";
}
osPrefix = "linux-" + arch;
}
break;
case Platform.MAC: {
//mac系统的os.arch都是ppc(老版本的mac是powerpc,已经基本不用)看不出系统位数,使用下面的参数表示
arch = System.getProperty("sun.arch.data.model");
osPrefix = "mac-" + arch;
}
break;
default: {
osPrefix = name.toLowerCase();
if ("x86".equals(arch)) {
arch = "i386";
}
if ("x86_64".equals(arch)) {
arch = "amd64";
}
int space = osPrefix.indexOf(" ");
if (space != -1) {
osPrefix = osPrefix.substring(0, space);
}
osPrefix += "-" + arch;
}
break;
}
return osPrefix;
}
public static String getOsName() {
String osName = "";
String osPrefix = getOsPrefix();
if (osPrefix.toLowerCase().startsWith("win32-x86")
|| osPrefix.toLowerCase().startsWith("win32-amd64")) {
osName = "win";
} else if (osPrefix.toLowerCase().startsWith("linux-i386")
|| osPrefix.toLowerCase().startsWith("linux-amd64")) {
osName = "linux";
} else if (osPrefix.toLowerCase().startsWith("mac-64")
|| osPrefix.toLowerCase().startsWith("mac-32")) {
osName = "mac";
}
return osName;
}
}
SpringBoot自带的Health Indicator
使用
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
配置
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=alwaysinfo.app.author=DigitalSonic
info.app.encoding=@project.build.sourceEncoding@
定义自己的Indicator实现HealthIndicator接口
@Component
public class MyIndicator implements HealthIndicator {
@Autowired
private CoffeeService coffeeService;
@Override
public Health health() {
// 用于判断的指标
long count = coffeeService.getCoffeeCount();
Health health;
if (count > 0) {
health = Health.up()
.withDetail("count", count)
.withDetail("message", "We have enough coffee.")
.build();
} else {
health = Health.down()
.withDetail("count", 0)
.withDetail("message", "We are out of coffee.")
.build();
}
return health;
}
}