最近一段时间由于服务器响应有些异常,所以花了半天做了一个简单实时监控页面。如下图
基本原理如下:
1、使用Filter拦截请求,采集服务器响应数据。
若是要收集响应状态码注意构造新的HttpServletResponse
package com.cmgame.ecms.statistic
import javax.servlet.http.HttpServletResponseWrapper
import javax.servlet.http.HttpServletResponse
/**
* 功能描述
* @author huzl
* @version 0.0.1, 12-7-30 下午3:11
*/
class StatusExposingServletResponse extends HttpServletResponseWrapper {
private Integer status = SC_OK;;
public Integer getStatus() {
return status
}
def StatusExposingServletResponse(HttpServletResponse response) {
super(response);
}
@Override
void sendError(int sc, String msg) {
super.sendError(sc, msg)
status = sc;
}
@Override
void sendError(int sc) {
super.sendError(sc)
status = sc;
}
@Override
void sendRedirect(String location) {
super.sendRedirect(location)
status = SC_MOVED_TEMPORARILY;
}
@Override
void setStatus(int sc) {
super.setStatus(sc)
status = sc;
}
@Override
void setStatus(int sc, String sm) {
super.setStatus(sc, sm)
status = sc;
}
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain){
long startTime = System.currentTimeMillis();
StatusExposingServletResponse response = new StatusExposingServletResponse(servletResponse);
Throwable exception = null;
try {
filterChain.doFilter(servletRequest,response)
} catch (Throwable e) {
exception = e;
throw e;
}finally{
statisticResult.completeRequest(servletRequest,response,exception,startTime);
}
}
若只统计错误状态码则区分Response Code是否大于400,小于400的都是正确响应
如200(OK),206(断点续传),301(永久重定向),302(临时重定向),304(内容未变),大于等于400的状态都是错误响应,计算响应速度区间和平均响应时间的代码我就不贴了
2、定时程序或线程把数据入库或保存到内存中。尽量不要使用java内嵌数据库如hsqldb,H2等,因为这些内存数据库运行时会把所有数据加到内存中,不太适合保存数据采集结果
3、使用Highcharts绘制监控页面
可以参照官网例子
http://www.highcharts.com/demo/dynamic-update,
var charts = new Array();
var serverCount = 6;
var lastTimes = new Array();
var max = ${params.int("max")?:120};
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
for (var i = 0; i < serverCount; i++) {
charts[i] = new Highcharts.Chart({
chart: {
renderTo: 'container' + i,
type: 'spline',
events: {
load: function() {
// set up the updating of the chart each second
var series = this.series;
var serverIndex = i;
lastTimes[serverIndex] = 0;
var loadData = function() {
$.getJSON("http://${request.serverName}:${request.serverPort}${request.contextPath}/toolkits/queryStatistics.gsp", {"lasTime":lastTimes[serverIndex],"proxy":true,"index":serverIndex,"max":max}, function(data) {
for (var k = 0; k < series.length; k++) {
for (var j = 0; j < data[k].length; j++) {
var point = data[k][j];
var isShift = series[k].data.length >= max;
console.log("series " + k + ".data.length=" + series[k].data.length);
var lastTime = 0;
if (series[k].data.length > 0)
lastTime = series[k].data[series[k].data.length - 1].x;
if (point[0] > lastTime)
series[k].addPoint([point[0],point[1]], true, isShift);
lastTimes[serverIndex] = point[0];
}
}
})
};
loadData();
setInterval(loadData, 60000);
}
}
},
title: {
text: '访问量实时监控'
},
xAxis: [
{
type: 'datetime',
tickPixelInterval: 120
}
],
yAxis: [
{
title: {
text: '总请求/分钟',
style: {
color: '#3E576F'
}
}
},
{
title: {
text: '平均响应时间',
style: {
color: '#00AA00'
}
},opposite:true
}
],
plotOptions: {
spline: {
marker:{
enabled: false,
states: {
hover: {
enabled: true,
symbol: 'circle',
radius: 5,
lineWidth: 1
}
}
}
}
},
tooltip: {
formatter: function() {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: true
},
exporting: {
enabled: false
},
series: [
{
name: '总请求数',
data: []
},
{
name: '错误请求数',
data: []
},
{
name: '平均响应时间',
yAxis:1,
data: []
}
]
});
}
})
需要注意的是:
1、在series的load事件中使用ajax定时加载数据,需要控制当前chart中的Point数据量,
series.addPoint(point, true, isShift);
当series中Point数量超过指定值,设定isShift为true,就可以移除第一个Point,防止浏览器内存占用太大无响应
2、ajax请求时只请求最新采集数据,所以每次加载采集数据后把最后时间保留下来,ajax请求时把当前chart中最后时间带上,获取最新数据
如果每秒采集一下数据并保存到数据库,可以扩充一下功能实现BI的数据挖掘和各维度的钻取该也不能。但由于是浏览器使用svg技术绘制曲线,采集的点太多时初次显示还是比较慢。