java实现对服务器的自动巡检邮件通知
作者:小卖铺的老爷爷
这篇文章主要为大家详细介绍了java实现对服务器的自动巡检邮件通知,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
1、需求
之前一直是手动的巡检,然后贴图,最近服务器数量大增,有点忙不过来了。因为一直用的java,对shell脚本不是特别了解,所以这次用java写了个小项目,实现对多服务器,多任务的巡检,巡检结果有故障的会通过邮件通知。
2、功能和效果
巡检的项目主要是服务,硬盘,内存等,命令可配置,巡检结果以日期和服务器为基准输出文件,错误信息通过邮件通知管理运维人员。
3、代码
action:
package com.save.action; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; import com.save.pojo.Cmd; import com.save.until.MailUtil; import com.save.until.PropertiesUtil; import com.save.until.SSHCommUtil; import com.save.until.WriteUntil; /** * 巡检任务 * @author zhangzhuo * */ public class InspAction { final static Logger logger = LoggerFactory.getLogger(InspAction.class); /* public static void main(String[] args) { InspAction n = new InspAction(); try { n.execute(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); logger.error("dd"); } }*/ /** * 执行巡检任务 * @param args */ public void execute() throws Exception{ List<Cmd> list = this.handlerData(); Set<String> mail = new HashSet<String>(); for (Cmd cmd : list) { String ip = cmd.getIp(); int port = 22; String localIp = null; int localPort = 0; int timeOut = 6000; String userName = cmd.getUsername(); String password = cmd.getPassword(); String server = cmd.getServer(); String[] cmds = cmd.getCmds(); String[] result = null; logger.info(ip+"执行巡检任务开始"); try { result = SSHCommUtil.execShellCmdBySSH(ip, port, localIp, localPort, timeOut, userName, password, cmds); } catch (Exception e) { e.printStackTrace(); logger.error(ip+"巡检,服务器连接不上"); mail.add(ip+" "+"巡检,服务器连接不上"); } Date date = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd"); String dateString = formatter.format(date); //1、服务存活验证 2、硬盘占用验证 3、巡检结果写入文件 if (result != null) { for (String string : result) { if (string.contains("ps -ef|grep java")||string.contains("ps -ef|grep mongo")||string.contains("ps -ef|grep redis")) { if (!string.contains(server)) { mail.add(ip+" "+server+"服务不存在"); } } if (string.contains("df -h")) { String patt = "^[5]\\d{1}\\%|[5-9]\\d{1}\\%|\\d{3,}\\%$"; String group = null; Pattern p = Pattern.compile(patt); Matcher m = p.matcher(string); while (m.find()) { group = m.group(); } if (!StringUtils.isBlank(group)) { mail.add(ip+" "+"硬盘占用超出预警线"); } } WriteUntil.createFile("E:\\save", dateString, "\\"+ip+".txt", string); } logger.info(ip+"巡检结束"); } } //发送故障邮件通知 if (!mail.isEmpty()||mail.size()!=0) { MailUtil.getInstance().sendMail(mail); } } /** * 数据处理 * @return */ private List<Cmd> handlerData(){ logger.info("开始加载需要巡检的服务器数据"); Cmd cmd = null; List<Cmd> list = new ArrayList<Cmd>(); Map map = PropertiesUtil.getInstance().getAllProperty(); Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> entry = it.next(); cmd =new Cmd(); cmd.setIp(entry.getKey()); Cmd cmd2 = JSON.parseObject(entry.getValue(), Cmd.class); String[] cmds = cmd2.getShell().split(","); cmd.setCmds(cmds); cmd.setServer(cmd2.getServer()); cmd.setUsername(cmd2.getUsername()); cmd.setPassword(cmd2.getPassword()); list.add(cmd); } logger.info("数据加载完毕"); return list; } }
pojo:
package com.save.pojo; public class Cmd { private String ip; private String username; private String password; private String shell; private String[] cmds; private String server; public String getServer() { return server; } public void setServer(String server) { this.server = server; } public String getShell() { return shell; } public void setShell(String shell) { this.shell = shell; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String[] getCmds() { return cmds; } public void setCmds(String[] cmds) { this.cmds = cmds; } }
工具类:
package com.save.until; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import com.jcraft.jsch.SocketFactory; /** * SSH创建与服务器连接工具类 * @author 张卓 * 2017-4-21 */ public class JSCHUtil { final static Logger logger = LoggerFactory.getLogger(JSCHUtil.class); private static JSch jsch = new JSch(); /** * 创建Session,并打开Session连接 * */ public static Session createSession(String dstIp, int dstPort, final String localIp, final int localPort, String userName, String password, final int timeOut) throws JSchException { //jsch.setKnownHosts("/home/foo/.ssh/known_hosts"); logger.info("开始连接:"+dstIp); // 建立一个SSH连接 Session session = jsch.getSession(userName, dstIp, dstPort); session.setPassword(password); Properties sshConfig = new Properties(); sshConfig.put("StrictHostKeyChecking", "no");//跳过主机检查 session.setConfig(sshConfig); // 此socket工厂用于创建目标主机的socket, // 并创建我们使用的这个socket字节流 session.setSocketFactory(new SocketFactory() { public OutputStream getOutputStream(Socket socket) throws IOException { return socket.getOutputStream(); } public InputStream getInputStream(Socket socket) throws IOException { return socket.getInputStream(); } public Socket createSocket(String host, int port) throws IOException, UnknownHostException { Socket socket = new Socket(); if (localIp != null) { socket.bind(new InetSocketAddress(InetAddress .getByName(localIp), localPort)); } socket.connect( new InetSocketAddress(InetAddress.getByName(host), port), timeOut); return socket; } }); session.connect(timeOut); return session; } }
package com.save.until; import java.util.Properties; import java.util.Set; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.mail.util.MailSSLSocketFactory; /** * 邮件发送 * @author zhangzhuo * */ public class MailUtil { final static Logger logger = LoggerFactory.getLogger(MailUtil.class); private static MailUtil instance = new MailUtil(); private MailUtil (){} public static MailUtil getInstance() { return instance; } public void sendMail(Set<String> mail) { String from = "XXX@qq.com";// 发件人电子邮箱 String host = "smtp.qq.com"; // 指定发送邮件的主机smtp.qq.com(QQ)|smtp.163.com(网易) Properties properties =new Properties(); properties.setProperty("mail.smtp.host", host);// 设置邮件服务器 properties.setProperty("mail.smtp.auth", "true");// 打开认证 try { //QQ邮箱需要下面这段代码,163邮箱不需要 MailSSLSocketFactory sf = new MailSSLSocketFactory(); sf.setTrustAllHosts(true); properties.put("mail.smtp.ssl.enable", "true"); properties.put("mail.smtp.ssl.socketFactory", sf); // 1.获取默认session对象 Session session = Session.getDefaultInstance(properties, new Authenticator() { public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("XXX@qq.com", "XXX"); // 发件人邮箱账号、授权码 } }); // 2.创建邮件对象 Message message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.addRecipient(Message.RecipientType.TO, new InternetAddress("XXX@qq.com")); message.setSubject("巡检故障通知"); StringBuffer sb = new StringBuffer(); for (String string : mail) { sb.append("<div>"+string+"</div><br/><hr/>"); } String content = sb.toString(); message.setContent(content, "text/html;charset=UTF-8"); Transport.send(message); logger.info("故障邮件发送成功"); } catch (Exception e) { e.printStackTrace(); } } }
package com.save.until; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.URI; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * 读取文件工具类 * @author zhangzhuo * */ public class PropertiesUtil { private Properties props; private URI uri; private static PropertiesUtil ourInstance = new PropertiesUtil("/config.properties"); public static PropertiesUtil getInstance() { return ourInstance; } public PropertiesUtil(String fileName){ readProperties(fileName); } private void readProperties(String fileName) { try { props = new Properties(); InputStream fis =getClass().getResourceAsStream(fileName); InputStreamReader re=new InputStreamReader(fis,"utf-8"); props.load(re); } catch (Exception e) { e.printStackTrace(); } } /** * 获取某个属性 */ public String getProperty(String key){ return props.getProperty(key); } /** * 获取所有属性,返回一个map,不常用 * 可以试试props.putAll(t) */ public Map getAllProperty(){ Map map=new HashMap(); Enumeration enu = props.propertyNames(); while (enu.hasMoreElements()) { String key = (String) enu.nextElement(); String value = props.getProperty(key); map.put(key, value); } return map; } /** * 在控制台上打印出所有属性,调试时用。 */ public void printProperties(){ props.list(System.out); } /** * 写入properties信息 */ public void writeProperties(String key, String value) { try { OutputStream fos = new FileOutputStream(new File(uri)); props.setProperty(key, value); // 将此 Properties 表中的属性列表(键和元素对)写入输出流 props.store(fos, "『comments』Update key:" + key); } catch (Exception e) { e.printStackTrace(); } } }
package com.save.until; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jcraft.jsch.Channel; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; /** * 执行Shell工具类 * @author zhangzhuo * */ public class SSHCommUtil { final static Logger logger = LoggerFactory.getLogger(SSHCommUtil.class); /** * SHH连接Linux Shell,返回结果 */ public static String[] execShellCmdBySSH(String dstIp, int dstport, String localIp, int localPort, int timeOut, String userName, String password, String... cmds) throws Exception { Session session = null; Channel channel = null; InputStream is = null; OutputStream os = null; try { session = JSCHUtil.createSession(dstIp, dstport, localIp, localPort, userName, password, timeOut); logger.info("开始创建channel通道!"); //创建一个channel类型的通道 channel = session.openChannel("shell"); // Enable agent-forwarding. // ((ChannelShell)channel).setAgentForwarding(true); // Choose the pty-type "vt102". // ((ChannelShell)channel).setPtyType("vt102"); // Set environment variable "LANG" as "ja_JP.eucJP". // ((ChannelShell)channel).setEnv("LANG", "ja_JP.eucJP"); channel.connect(); is = channel.getInputStream(); os = channel.getOutputStream(); String[] result = new String[cmds.length]; for (int i = 0; i < cmds.length; i++) { result[i] = sendCommand(is, os, cmds[i]); } return result; } catch (JSchException e) { if (e.getMessage().contains("Auth fail")) { logger.error(dstIp+"服务器验证失败"); throw new Exception("Auth error"); } else { logger.error(dstIp+"服务器连接失败"); throw new Exception("Connect error"); } } catch (Exception e) { throw e; } finally { try { is.close(); } catch (IOException e) { } try { os.close(); } catch (IOException e) { } channel.disconnect(); session.disconnect(); } } /** *执行Shell脚本并返回结果 * */ private static String sendCommand(InputStream is, OutputStream os, String cmd) throws IOException { logger.info("开始执行脚本!"); os.write(cmd.getBytes()); os.flush(); StringBuffer sb = new StringBuffer(); int beat = 0; while (true) { if (beat > 3) { break; } if (is.available() > 0) { byte[] b = new byte[is.available()]; is.read(b); sb.append(new String(b)); beat = 0; } else { if (sb.length() > 0) { beat++; } try { Thread.sleep(sb.toString().trim().length() == 0 ? 1000 : 300); } catch (InterruptedException e) { } } } return sb.toString(); } }
package com.save.until; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; /** * SSH工具类 * */ public class SSHExcuteCommandHelper { Session session = null; ChannelExec openChannel = null; /** * @param host 主机ip * @param name 用户名 * @param pwd 密码 * @param port ssh端口 */ public SSHExcuteCommandHelper(String host, String user, String pwd, int port) { JSch jsch = new JSch(); try { session = jsch.getSession(user, host, port); java.util.Properties config = new java.util.Properties(); config.put("StrictHostKeyChecking", "no"); session.setTimeout(1000); session.setConfig(config); session.setPassword(pwd); } catch (JSchException e) { e.printStackTrace(); } } /** * 是否连接成功,调用如果不需要调用execCommand方法那么必须调用 disconnect方法关闭session * @return */ public boolean canConnection(){ try { session.connect(); return true; } catch (JSchException e) { e.printStackTrace(); return false; } } /** * 关闭连接 */ public void disconnect(){ if (openChannel != null && !openChannel.isClosed()) { openChannel.disconnect(); } if (session != null && session.isConnected()) { session.disconnect(); } } /** * 执行命令 * @param command * @return */ public String execCommand(String command) { StringBuffer result = new StringBuffer(); try { if(!session.isConnected()){ session.connect(); } openChannel = (ChannelExec) session.openChannel("exec"); openChannel.setCommand(command); //int exitStatus = openChannel.getExitStatus(); openChannel.connect(); InputStream in = openChannel.getInputStream(); BufferedReader reader = new BufferedReader( new InputStreamReader(in)); String tmpStr = ""; while ((tmpStr = reader.readLine()) != null) { result.append(new String(tmpStr.getBytes("gbk"), "UTF-8")).append("\n"); } } catch (Exception e) { e.printStackTrace(); result.append(e.getMessage()); } finally { disconnect(); } return result.toString(); } /** * 解析 * @param result * @return */ public List<List<String>> parseResult(String result){ List<List<String>> parseResult = new ArrayList<List<String>>(); List<String> list = null; // for (String line : result.split("\n")) { list = new ArrayList<String>(); String[] columns = {}; //这个是针对df命令的 [Mounted on] 其实就一个,如果用空格就会分割出两个 if(line.contains("Mounted ")){ columns = line.replace("Mounted ", "Mounted-").split(" "); }else{ columns = line.split(" "); } for (String column : columns) { if (!" ".equals(column) && !"".equals(column)) { list.add(column); } } parseResult.add(list); } return parseResult; } //测试 /* public static void main(String args[]) { SSHExcuteCommandHelper execute = new SSHExcuteCommandHelper("192.168.175.128", "root", "123456", 22); System.out.println("是否连接成功"+execute.canConnection()); String s = execute.execCommand("free -m"); System.out.println("解析前"); System.out.println(s); System.out.println("解析后"); List<List<String>> parseResult = execute.parseResult(s); for (List<String> l : parseResult) { System.out.println(l); } }*/ }
package com.save.until; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.save.action.InspAction; /** * * @author zhangzhuo * */ public class WriteUntil { final static Logger logger = LoggerFactory.getLogger(WriteUntil.class); /** * 新建文件夹,并创建文件写入数据 */ public static void createFile(String basePath,String filePath, String filename, String input) { String[] dirs = filePath.split("/"); String tempPath = basePath; for (String dir : dirs) { if (null == dir || "".equals(dir)) continue; tempPath += "\\" + dir; } //文件夹判断 File dir = new File(tempPath);//"d:\\test_dir" if (dir.exists()) { if (dir.isDirectory()) { logger.info("文件夹存在"); } else { logger.info("同名文件存在,无法创建目录"); } } else { logger.info("文件夹不存在,开始创建"); dir.mkdirs(); } //文件判断 File file = new File(tempPath+filename);//"d:\\test_file.txt" if (file.exists()) { logger.info(filename+"已存在"); } else { logger.info(filename+"文件不存在,开始创建"); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } //写入数据 //方法一、每次写入覆盖之前的 /* try { FileOutputStream fos = new FileOutputStream(tempPath+filename); fos.write(input.getBytes()); fos.close(); } catch (Exception e) { e.printStackTrace(); }*/ try { FileOutputStream fos = new FileOutputStream(tempPath+filename, true); fos.write(input.getBytes()); fos.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //测试 /* public static void main(String[] args) { //createFile("E:\\log", "2014/16/2/", "\\2020.txt", "hahha"); Date date = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd"); String dateString = formatter.format(date); System.out.println(dateString); }*/ }
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> <!-- 配置作业类 --> <bean id="InspAction" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <bean class="com.save.action.InspAction" /> </property> <property name="targetMethod" value="execute" /> <property name="concurrent" value="false" /><!-- 作业不并发调度 --> </bean> <!-- 配置触发器 --> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="InspAction" /> <!-- 每天7:00运行一次 --> <property name="cronExpression" value="0 0 07 * * ?" /> </bean> <!-- 配置调度工厂 --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> </bean> </beans>
config.properties
#测试用服务器 192.168.175.128={"username":"root","password":"123456","shell":"ps -ef|grep mongo\n,df -h\n, free -m\n, top\n","server":"mongod"} 192.168.175.129={"username":"root","password":"123456","shell":"ps -ef|grep redis\n,df -h\n, free -m\n, top\n","server":"mongod"} log4j.properties
#指定根Logger,及日志输出级别 #大于等于该级别的日志将被输出( DEBUG < INFO < WARN < ERROR < FATAL ),设为OFF可以关闭日志 log4j.rootLogger=INFO, A1,A2 #指定log输出目的,这里设为输出日志到指定目录的文件my.log中 log4j.appender.A1=org.apache.log4j.FileAppender log4j.appender.A1.File=E:\\save\\log\\xj.log #指定日志信息的格式 log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%r %d{yyyy-MM-dd HH:mm:ss} %c %p -%m%n #把A2输出到控制台 log4j.appender.A2=org.apache.log4j.ConsoleAppender log4j.appender.A2.layout=org.apache.log4j.PatternLayout log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %c %p -%m%n
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.save</groupId> <artifactId>save-xj</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>save-xj Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <!-- SSH连接 --> <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.53</version> </dependency> <!-- json转换 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> <!-- Spring的包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>3.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>3.1.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.0.5.RELEASE</version> </dependency> <!-- 定时任务 --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>1.8.5</version> </dependency> <!-- 日志的包 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <!-- 邮件 --> <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.4.4</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> </dependencies> <build> <plugins> <!-- 配置Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。