转载自:http://blog.sina.com.cn/s/blog_7d553bb50102w9jj.html
2016年3月14日 11:32:40
1. 项目背景
监控整个测试环境的核心http服务,服务的进程(自动重启),服务器的资源(cpu,mem,disk
)
2. 技术框架
服务端:
Java / Spring Boot / JPA / Mysql
参考:
前端:
JavaScript / Css /Html / Ajax / Jquery / Bootstrap
3. 系统架构
功能模块
技术架构
4 系统原型
5 核心功能实现
http执行引擎
package com.femon.engine;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.femon.config.Constant;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;
public class OKHttpEngine {
private static final String LOGIN_API_ALL = "http://user.wacaiyun.com/login_api/all";
public static void xmain(String[] args) {
String jsonParamsStr = "{"password":"" + Constant.LOGIN_PASSWORD_APP + "","username":""
+ Constant.LOGIN_USERNAME_APP + ""}";
System.out.println(jsonString2paramsMap(jsonParamsStr));
String loginResult = postJson(LOGIN_API_ALL, jsonParamsStr);
System.out.println("postJson====================================================\n" + loginResult);
String cardCountUrl = "http://192.168.3.128:7103/cd/card/count";
Map<String, String> headerMap = new HashMap<String, String>();
Map<String, String> appLoginServiceNodeOutput = appLoginServiceNode(Constant.LOGIN_USERNAME_APP,
Constant.LOGIN_PASSWORD_APP);
String token = appLoginServiceNodeOutput.get("token");
String refreshToken = appLoginServiceNodeOutput.get("refreshToken");
headerMap.put("accessToken", token);
headerMap.put("refreshToken", refreshToken);
headerMap.put("appPlatform", "21");
headerMap.put("appVersion", "5.3.0");
headerMap.put("channelType", "1,2,3");
headerMap.put("deviceId", "14FBE92C0BA95144E0D85867A29D4947");
headerMap.put("mcCode", "21000020");
headerMap.put("systemInfo", "iphone 6s plus");
String qresult = getWithQueryParams(cardCountUrl, headerMap);
System.out.println("qresult==============================================" + qresult);
String hresult = getWithHeaderParams(cardCountUrl, headerMap);
System.out.println("hresult==============================================" + hresult);
/ post form
String h5LoginUrl = "http://8.wacaiyun.com/finance/h5/finance/h5/ulogin.action";
String formParams = "loginMedium=15868187925&loginPwd=123456";
String formResult = postForm(h5LoginUrl, formParams);
System.out.println("formResult==============================================" + formResult);
}
public static Map<String, String> appLoginServiceNode(String username, String password) {
Map<String, String> outputMap = new HashMap<String, String>();
String appLoginUrl = LOGIN_API_ALL;
String jsonParamsStr = "{"password":"" + password + "","username":"" + username + ""}";
String loginResponse = postJson(appLoginUrl, jsonParamsStr);
Map loginResponseMap = com.alibaba.fastjson.JSON.parseObject(loginResponse, Map.class);
String code = (String) loginResponseMap.get("code");
JSONArray userJSONArray = (JSONArray) loginResponseMap.get("users");
Map usersMap = (Map) userJSONArray.get(0);
if ("0".equals(code)) {
outputMap.put("token", (String) usersMap.get("token"));
outputMap.put("refreshToken", (String) usersMap.get("refreshToken"));
}
return outputMap;
}
public static String get(String url) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response;
String result = null;
try {
response = client.newCall(request).execute();
result = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static String getWithQueryParams(String url, Map<String, String> queryMap) {
String queryString = paramsMap2QueryStr(queryMap);
String qurl = url + "?" + queryString;
return get(qurl);
}
public static String getWithHeaderParams(String url, Map<String, String> headerMap) {
OkHttpClient client = new OkHttpClient();
// 用平台token替换填写的失效token
String loginUrl = "http://user.wacaiyun.com/login_api/all";
Map<String, String> appLoginServiceNodeOutput = appLoginServiceNode(Constant.LOGIN_USERNAME_APP,
Constant.LOGIN_PASSWORD_APP);
String token = appLoginServiceNodeOutput.get("token");
String refreshToken = appLoginServiceNodeOutput.get("refreshToken");
headerMap.put("accessToken", token);
headerMap.put("refreshToken", refreshToken);
Builder builder = new Request.Builder();
for (String name : headerMap.keySet()) {
String value = headerMap.get(name);
builder.addHeader(name, value);
}
builder.url(url);
Request request = builder.build();
Response response;
String result = null;
try {
response = client.newCall(request).execute();
result = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static String postWithHeaderParams(String apiUrl, String jsonParamsStr) {
OkHttpClient client = new OkHttpClient();
Map<String, String> headerMap = jsonString2paramsMap(jsonParamsStr);
// 用平台token替换填写的失效token
String loginUrl = "http://user.wacaiyun.com/login_api/all";
Map<String, String> appLoginServiceNodeOutput = appLoginServiceNode(Constant.LOGIN_USERNAME_APP,
Constant.LOGIN_PASSWORD_APP);
String token = appLoginServiceNodeOutput.get("token");
String refreshToken = appLoginServiceNodeOutput.get("refreshToken");
headerMap.put("accessToken", token);
headerMap.put("refreshToken", refreshToken);
Builder builder = new Request.Builder();
for (String name : headerMap.keySet()) {
String value = headerMap.get(name);
builder.addHeader(name, value);
}
RequestBody requestBody = RequestBody.create(JSON, "");
builder.url(apiUrl).post(requestBody);
Request request = builder.build();
Response response;
String result = null;
try {
response = client.newCall(request).execute();
result = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static String postJson(String url, String json) {
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder().url(url).post(body).build();
String result = null;
Response response;
try {
response = client.newCall(request).execute();
result = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static String postForm(String url, String formParams) {
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(FORM, formParams);
Request request = new Request.Builder().url(url).post(body).build();
String result = null;
Response response;
try {
response = client.newCall(request).execute();
result = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private static String paramsMap2QueryStr(Map<String, String> paramsMap) {
StringBuilder qstr = new StringBuilder();
for (String key : paramsMap.keySet()) {
qstr.append(key);
qstr.append("=");
qstr.append(URLEncoder.encode(paramsMap.get(key)));
// qstr.append(paramsMap.get(key));
qstr.append("&");
}
return qstr.toString();
}
private static Map<String, String> jsonString2paramsMap(String jsonString) {
Map<String, String> map = com.alibaba.fastjson.JSON.parseObject(jsonString, Map.class);
return map;
}
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public static final MediaType FORM = MediaType.parse("application/x-www-form-urlencoded;charset=utf-8");
}
创建服务前端
#include("header.html")
<div class="nav-guide">
<a href="/service">服务管理</a> > 创建服务
</div>
<hr width="96%">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">测试环境</label>
<div class="col-sm-3">
<select name="s-host" class="form-control ">
<option value="1" selected="selected">一套</option>
<option value="2">二套</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">域名</label>
<div class="col-sm-3">
<input type="text" class="form-control " name="s-hostname"
value="http://8.wacaiyun.com">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">服务名称</label>
<div class="col-sm-3">
<input type="text" class="form-control " name="s-name">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">URL</label>
<div class="col-sm-3">
<input type="text" class="form-control " name="s-url">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">请求方法</label>
<div class="col-sm-3">
<select name="s-method" class="form-control ">
<option value="GET">GET</option>
<option value="POST">POST</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">期望输出</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="s-output"
placeholder='"code":0'></input>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">参数类型</label>
<div class="col-sm-3">
<select name="param-type" class="form-control">
<option selected="selected" value="1">普通参数</option>
<option value="2">json串</option>
<option value="3">header参数</option>
<option value="4">body参数</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">参数内容</label>
<div class="col-sm-3">
<textarea name="param-content" class="form-control"
placeholder='{"username":"boshu","password":"123456"}'></textarea>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">负责人</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="s-owner"></input>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">邮箱</label>
<div class="col-sm-3">
<input type="text" class="form-control" name="s-email"
placeholder='meiyu@wacai.com'></input>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10 " style="margin-top: 30px">
<a class="btn btn-success col-sm-1" id="bt-service-new">创 建</a>
</div>
</div>
</div>
#include("footer.html")
<script type="text/javascript">
$(function() {
serviceSelection();
$("select[name='s-host']").change(function() {
var item = $(this).val();
if (item == '1') {
$("input[name='s-hostname']").val('http://8.wacaiyun.com');
}
else {
$("input[name='s-hostname']").val('http://qa8.wacaiyun.com');
}
});
$("#bt-service-new").on('click', function() {
var host = $("input[name='s-hostname']").val();
var hostCode = $("select[name='s-host']").val();
var serviceName = $("input[name='s-name']").val();
var requestUrl = $("input[name='s-url']").val();
var requestMethod = $("select[name='s-method']").val();
var expectOutput = $("input[name='s-output']").val();
var owner = $("input[name='s-owner']").val();
var email = $("input[name='s-email']").val();
// 1: 普通参数 2:json串
var paramType = parseInt($("select[name='param-type']").val());
var paramContent = $("textarea[name='param-content']").val();
if (paramContent == '') {
paramType = 0; //表示没有传参数
}
if (serviceName != '' && requestUrl != '' && expectOutput != '' && owner != '' && email != '') {
$.post("/service/new", {
name : serviceName,
host : host,
hostCode : hostCode,
requestUrl : requestUrl,
expect : expectOutput,
method : requestMethod,
paramsType : paramType,
paramsMap : paramContent,
owner : owner,
email : email
}, function(data) {
if (data) {
window.location.href = "/service";
}
else {
alert("Fail");
}
});
}
else {
alert("信息不全!");
}
});
});
</script>
进程重启
当进程down之后,执行重启脚本:
package com.femon.engine;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import java.io.OutputStream;
import java.io.PrintStream;
public class ShellChannelExecutor {
public static void runCommands(String username, String password, String ip, int port, String command) {
try {
JSch jsch = new JSch();
Session session = jsch.getSession(username, ip, port);
session.setPassword(password);
setUpHostKey(session);
session.connect();
Channel channel = session.openChannel("shell");// only shell
channel.setOutputStream(System.out);
OutputStream outputStream = channel.getOutputStream();
PrintStream shellStream = new PrintStream(outputStream); // printStream
// for
// convenience
channel.connect();
shellStream.println(command);
shellStream.flush();
Thread.sleep(5000);
channel.disconnect();
session.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void setUpHostKey(Session session) {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
}
}
远程SSH登录执行shell代码:
package com.femon.engine;
import com.jcraft.jsch.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import org.springframework.util.StringUtils;
public class RemoteShell {
private Session session;
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
public String runRemoteShell(String command) {
if (StringUtils.isEmpty(command))
return null;
StringBuilder stringBuffer;
String result = null;
BufferedReader reader = null;
Channel channel = null;
try {
stringBuffer = new StringBuilder();
channel = session.openChannel("exec"); // exec, shell
((ChannelExec) channel).setCommand(command);
channel.connect(3000);
InputStream in = channel.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));
String buf;
while ((buf = reader.readLine()) != null) {
stringBuffer.append(buf.trim()).append(LINE_SEPARATOR);
}
result = stringBuffer.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
if (channel != null)
channel.disconnect();
}
return result;
}
public boolean connect(String user, String passwd, String host) {
JSch jsch = new JSch();
try {
session = jsch.getSession(user, host, 22);
session.setPassword(passwd);
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
} catch (JSchException e) {
e.printStackTrace();
return false;
}
return true;
}
public void disconnect() {
try {
session.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
邮件通知
package com.femon.engine;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
public class MailClient {
private static final String USERNAME = "*******";
private static final String PASSWORD = "*******";
public static void sendMail(String to, String title, String content) {
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.host", "mail.xxxx.com");
props.put("mail.smtp.port", "25");
Session session = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(USERNAME, PASSWORD);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("qa_notification@xxx.com"));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
message.setSubject(title);
message.setText(content);
// MimeMultipart类是一个容器类,包含MimeBodyPart类型的对象
Multipart mainPart = new MimeMultipart();
MimeBodyPart messageBodyPart = new MimeBodyPart();// 创建一个包含附件内容的MimeBodyPart
// 设置HTML内容
messageBodyPart.setContent(content, "text/html; charset=utf-8");
mainPart.addBodyPart(messageBodyPart);
message.setContent(mainPart);
Transport.send(message);
System.out.println("Done");
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
监控数据统计jpa代码
package com.femon.dao;
import com.femon.entity.ProcessData;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.Date;
import java.util.List;
public interface ProcessDataDao extends PagingAndSortingRepository<ProcessData, Integer> {
@Query(value = "select * from fm_process_data where process_id = ?1 order by sample_time desc limit 1", nativeQuery = true)
ProcessData findOneByProcessId(int processId);
@Query(value = "select * from fm_process_data where process_id = ?1 order by sample_time desc limit 40", nativeQuery = true)
List<ProcessData> findByProcessId(int processId);
@Query(value = "select count(*) from fm_process_data where process_id = ?1 and sample_time like ?2% and state = 1 order by sample_time desc", nativeQuery = true)
int countOfTodaySuccess(int processId, String curDate);
@Query(value = "select count(*) from fm_process_data where process_id = ?1 and sample_time like ?2% and state = 0 order by sample_time desc", nativeQuery = true)
int countOfTodayFailure(int processId, String curDate);
@Query(value = "select count(*) FROM femon.fm_process_data as pd where process_id=?1 and state=?2 and datediff(now(),pd.sample_time )<=?3 order by sample_time desc", nativeQuery = true)
int countStateOfDays(int processId, int state, int days);
@Query(value = "select * FROM femon.fm_process_data as pd where process_id=?1 and state=0 and datediff(now(),pd.sample_time )<=?3 order by sample_time desc", nativeQuery = true)
List<ProcessData> findFailsByProcessIdOfDays(int processId, int days);
}