
1. 添加依赖Actuator是Springboot提供的用来对应用系统进行自省和监控的功能模块,借助于Actuator开发者可以很方便地对应用系统某些监控指标进行查看、统计等。
部署简单、直接调接口拿值、数据较分散,需处理
2. application.yaml配置org.springframework.boot spring-boot-starter-actuator
#actuator监控配置
management:
endpoints:
web:
exposure:
#默认值访问health,info端点 用*可以包含全部端点
include: '*'
#配置路径
base-path: /system/actuator
endpoint:
health:
#获得健康检查中所有指标的详细信息
show-details: always
3. 启动项目,访问
以下显示的路径,都是可以被健康检查的指标
其中谷歌浏览器显示格式为 JSON,是因为下载了 JSON-handle 插件
访问路径:项目路径+actuator配置路径:
Actuator健康项:
可监控的信息包含:应用状态、内存、线程、堆栈等等,比较全面的监控了 Spring Boot 应用的整个生命周期。
在Spring Boot Actuator的基础上提供简洁的可视化WEB UI
SpringBoot Admin 分为服务端(spring-boot-admin-server)和客户端(spring-boot-admin-client),客户端就是指需要被监控的应用端(项目启动位置)
1. Admin Server 端新建一个项目模块,项目依赖
de.codecentric spring-boot-admin-starter-server 2.1.0 org.springframework.boot spring-boot-starter-web
application.yaml 配置文件
# 配置tomcat访问端口 server: port: 8000
启动类
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
public class AdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(AdminServerApplication.class, args);
}
}
访问浏览器 http://localhost:8000
在原本项目基础上,添加依赖
de.codecentric spring-boot-admin-starter-client 2.1.0 org.springframework.boot spring-boot-starter-web
配置文件
# 配置访问端口
server:
port: 20211
spring:
application:
name: Admin Client
# 配置AdminServer的地址
boot:
admin:
client:
url: http://localhost:8000
# 打开客户端的监控
management:
endpoints:
web:
exposure:
include: *
客户端启动类
@SpringBootApplication
public class AdminClientApplication {
public static void main(String[] args) {
SpringApplication.run(AdminClientApplication.class, args);
}
}
先启动服务端,再启动客户端
启动 Client 端,Admin 服务端会自动检查到客户端的变化,并展示其应用
页面会展示被监控的服务列表,点击详项目名称会进入此应用的详细监控信息
1. pom.xml依赖基于JNA的免费的本地操作系统和Java的硬件信息库,可以跨平台,获取监控信息简单
2. 接口返回数据 3. 代码com.github.oshi oshi-core 5.6.0
Controller
import com.lzby.tqj.dto.AjaxResult;
import com.lzby.tqj.entity.Server;
import io.swagger.*;
import org.springframework.web.bind.annotation.*;
@CrossOrigin(origins = "*", maxAge = 3600)
@Api(tags = "系统-服务器监控")
@RestController
@RequestMapping("/monitor/server")
public class ServerController {
@ApiOperation(value = "获得服务器相关信息", notes = "获得服务器相关信息")
@PostMapping(value = "/getInfo")
public AjaxResult getInfo() throws Exception {
Server server = new Server();
server.copyTo();
return AjaxResult.success(server);
}
}
entity - Server
package com.lzby.tqj.entity;
import com.lzby.tqj.common.IpUtils;
import com.lzby.tqj.common.util.Arith;
import com.lzby.tqj.entity.server.*;
import oshi.*;
import java.net.InetAddress;
import java.util.*;
public class Server {
private static final int OSHI_WAIT_SECOND = 1000;
// CPU相关信息
private Cpu cpu = new Cpu();
// 內存相关信息
private Mem mem = new Mem();
// JVM相关信息
private Jvm jvm = new Jvm();
// 服务器相关信息
private Sys sys = new Sys();
// 磁盘相关信息
private List sysFiles = new linkedList();
public Cpu getCpu() {
return cpu;
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
public Mem getMem() {
return mem;
}
public void setMem(Mem mem) {
this.mem = mem;
}
public Jvm getJvm() {
return jvm;
}
public void setJvm(Jvm jvm) {
this.jvm = jvm;
}
public Sys getSys() {
return sys;
}
public void setSys(Sys sys) {
this.sys = sys;
}
public List getSysFiles() {
return sysFiles;
}
public void setSysFiles(List sysFiles) {
this.sysFiles = sysFiles;
}
public void copyTo() throws Exception {
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
setCpuInfo(hal.getProcessor());
setMemInfo(hal.getMemory());
setSysInfo();
setJvmInfo();
setSysFiles(si.getOperatingSystem());
}
private void setCpuInfo(CentralProcessor processor) {
// CPU信息
long[] prevTicks = processor.getSystemCpuLoadTicks();
Util.sleep(OSHI_WAIT_SECOND);
long[] ticks = processor.getSystemCpuLoadTicks();
long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
cpu.setCpuNum(processor.getLogicalProcessorCount());
cpu.setTotal(totalCpu);
cpu.setSys(cSys);
cpu.setUsed(user);
cpu.setWait(iowait);
cpu.setFree(idle);
}
private void setMemInfo(GlobalMemory memory) {
mem.setTotal(memory.getTotal());
mem.setUsed(memory.getTotal() - memory.getAvailable());
mem.setFree(memory.getAvailable());
}
private void setSysInfo() {
Properties props = System.getProperties();
sys.setComputerName(getHostName());
sys.setComputerIp(getHostIp());
sys.setOsName(props.getProperty("os.name"));
sys.setOsArch(props.getProperty("os.arch"));
sys.setUserDir(props.getProperty("user.dir"));
}
private void setJvmInfo() throws UnknownHostException {
Properties props = System.getProperties();
jvm.setTotal(Runtime.getRuntime().totalMemory());
jvm.setMax(Runtime.getRuntime().maxMemory());
jvm.setFree(Runtime.getRuntime().freeMemory());
jvm.setVersion(props.getProperty("java.version"));
jvm.setHome(props.getProperty("java.home"));
}
private void setSysFiles(OperatingSystem os) {
FileSystem fileSystem = os.getFileSystem();
List fsArray = fileSystem.getFileStores();
for (OSFileStore fs : fsArray) {
long free = fs.getUsableSpace();
long total = fs.getTotalSpace();
long used = total - free;
SysFile sysFile = new SysFile();
sysFile.setDirName(fs.getMount());
sysFile.setSysTypeName(fs.getType());
sysFile.setTypeName(fs.getName());
sysFile.setTotal(convertFileSize(total));
sysFile.setFree(convertFileSize(free));
sysFile.setUsed(convertFileSize(used));
sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100));
sysFiles.add(sysFile);
}
}
public String convertFileSize(long size) {
long kb = 1024;
long mb = kb * 1024;
long gb = mb * 1024;
if (size >= gb) {
return String.format("%.1f GB", (float) size / gb);
} else if (size >= mb) {
float f = (float) size / mb;
return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
} else if (size >= kb) {
float f = (float) size / kb;
return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
} else {
return String.format("%d B", size);
}
}
public static String getHostIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
return "127.0.0.1";
}
public static String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
}
return "未知";
}
}
entity - Cpu
package com.lzby.tqj.entity.server;
import com.lzby.tqj.common.util.Arith;
public class Cpu
{
// 核心数
private int cpuNum;
// CPU总的使用率
private double total;
// CPU系统使用率
private double sys;
// CPU用户使用率
private double used;
// CPU当前等待率
private double wait;
// CPU当前空闲率
private double free;
public int getCpuNum(){
return cpuNum;
}
public void setCpuNum(int cpuNum){
this.cpuNum = cpuNum;
}
public double getTotal(){
return Arith.round(Arith.mul(total, 100), 2);
}
public void setTotal(double total){
this.total = total;
}
public double getSys(){
return Arith.round(Arith.mul(sys / total, 100), 2);
}
public void setSys(double sys){
this.sys = sys;
}
public double getUsed(){
return Arith.round(Arith.mul(used / total, 100), 2);
}
public void setUsed(double used){
this.used = used;
}
public double getWait(){
return Arith.round(Arith.mul(wait / total, 100), 2);
}
public void setWait(double wait){
this.wait = wait;
}
public double getFree(){
return Arith.round(Arith.mul(free / total, 100), 2);
}
public void setFree(double free){
this.free = free;
}
}
entity - Mem
package com.lzby.tqj.entity.server;
import com.lzby.tqj.common.util.Arith;
public class Mem{
// 内存总量
private double total;
// 已用内存
private double used;
// 剩余内存
private double free;
public double getTotal(){
return Arith.div(total, (1024 * 1024 * 1024), 2);
}
public void setTotal(long total){
this.total = total;
}
public double getUsed(){
return Arith.div(used, (1024 * 1024 * 1024), 2);
}
public void setUsed(long used){
this.used = used;
}
public double getFree(){
return Arith.div(free, (1024 * 1024 * 1024), 2);
}
public void setFree(long free){
this.free = free;
}
public double getUsage(){
return Arith.mul(Arith.div(used, total, 4), 100);
}
}
entity - Jvm
package com.lzby.tqj.entity.server;
import java.lang.management.ManagementFactory;
import com.lzby.tqj.common.util.Arith;
import com.lzby.tqj.common.util.DateUtils;
public class Jvm{
// 当前JVM占用的内存总数(M)
private double total;
// JVM最大可用内存总数(M)
private double max;
// JVM空闲内存(M)
private double free;
// JDK版本
private String version;
// JDK路径
private String home;
public double getTotal(){
return Arith.div(total, (1024 * 1024), 2);
}
public void setTotal(double total){
this.total = total;
}
public double getMax(){
return Arith.div(max, (1024 * 1024), 2);
}
public void setMax(double max){
this.max = max;
}
public double getFree(){
return Arith.div(free, (1024 * 1024), 2);
}
public void setFree(double free){
this.free = free;
}
public double getUsed(){
return Arith.div(total - free, (1024 * 1024), 2);
}
public double getUsage(){
return Arith.mul(Arith.div(total - free, total, 4), 100);
}
// 获取JDK名称
public String getName(){
return ManagementFactory.getRuntimeMXBean().getVmName();
}
public String getVersion(){
return version;
}
public void setVersion(String version){
this.version = version;
}
public String getHome(){
return home;
}
public void setHome(String home){
this.home = home;
}
// JDK启动时间
public String getStartTime(){
return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate());
}
// JDK运行时间
public String getRunTime()
{
return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate());
}
}
entity - Jvm
package com.lzby.tqj.entity.server;
import lombok.Data;
@Data
public class Sys{
// 服务器名称
private String computerName;
// 服务器Ip
private String computerIp;
// 项目路径
private String userDir;
// 操作系统
private String osName;
// 系统架构
private String osArch;
}
工具类 Arith
package com.lzby.tqj.common.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Arith{
private static final int DEF_DIV_SCALE = 10;
private Arith(){}
public static double add(double v1, double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doublevalue();
}
public static double mul(double v1, double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doublevalue();
}
public static double div(double v1, double v2, int scale){
if (scale < 0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
if (b1.compareTo(BigDecimal.ZERO) == 0){
return BigDecimal.ZERO.doublevalue();
}
return b1.divide(b2, scale, RoundingMode.HALF_UP).doublevalue();
}
public static double round(double v, int scale){
if (scale < 0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, RoundingMode.HALF_UP).doublevalue();
}
}