usually, I wanna know how much load created at certain point of time on my async system, and see how much impact the concurrent load bring to it. So I made a simple Create WaterFall Chart to help me to understand these more clearly.
This is written in Ruby,sorry java guys, it is faster for me to write ruby code than java, but i like java gradually :-)
I am using
Google Chart API,
Googlecharts,
TinyURL services, Thanks you for providing these!
A quick snapshot for waterfall chart(click to see big):
Test code:
require 'gchart'
require 'time'
require 'open-uri'
require 'uri'
require 'accessDB'
#Get start and end time stamp from DB
startArray = ["2010-09-26 16:58:37.693","2010-09-26 16:58:38.223","2010-09-26 16:58:38.833",
"2010-09-26 16:58:39.600","2010-09-26 16:58:40.210","2010-09-26 16:58:40.833","2010-09-26 16:58:55.740"];
endArray = ["2010-09-26 16:59:56.543","2010-09-26 16:59:58.183","2010-09-26 17:00:04.217",
"2010-09-26 17:00:09.370","2010-09-26 17:00:10.217","2010-09-26 17:00:19.323","2010-09-26 17:00:25.027"];
Gchart_string = prepareData(startArray, endArray )
Gchart_url = waterFallUrl(Gchart_string)
puts minifyUrl(Gchart_url)
Prepare Google chart Data and para:
def prepareData(starttime, endtime )
if starttime.length > 400
puts "\nExceed the maxium number(400) of records."
puts "You can try to split them to display.Press Enter to exit..."; $stdout.flush
gets
break;
end
# convert Time Object string to Time object Int
starttime.collect! { |time1|
Time.parse("#{time1}").to_i;
}
starttime.sort!
endtime.collect! { |time2|
Time.parse("#{time2}").to_i;
}
endtime.sort!
# prepare data in array, convert to string used by Gchart
flag = starttime[0]
starttime.collect! { |time1|
time1-flag;
}
endtime.collect! { |time2|
time2-flag;
}
if endtime.last > 3600
puts "\nWarning: Duration is too long to display well for Chart(3600s is recommended).";
puts "You can split the data to display."
end
line_xy_string = "";
scale_num = 1;
cata = 10;
while endtime.last > cata
cata = cata + 10 ;
end
scale_num = cata/starttime.length
starttime.length.times { |i|
line_xy_string << starttime[i].to_s;
line_xy_string << ",";
line_xy_string << endtime[i].to_s;
line_xy_string << "|";
line_xy_string << ((i+1)*scale_num).to_s + "," + ((i+1)*scale_num).to_s;
# ignore the last "|"
if ((i+1) < starttime.length)
line_xy_string << "|";
end
}
line_xy_string << "&chds=";
line_xy_string << "0,#{cata}";
line_xy_string << "&chxt=x,x,y"
line_xy_string << "&chxr=0,0,#{cata}"
line_xy_string << "&chxl=1:|Duration(sec)"
return line_xy_string;
end
Get Google Chart URL:
def waterFallUrl(chart_string)
output_url = Gchart.line_xy(:size => '700x400',
:title => "WaterFall Charts",
:custom => "chd=t:#{chart_string}")
#"&chxr=0,0" is automatic added to the end of string,
#remove it due to already identify one in chart_string
output_url.chomp!("&chxr=0,0");
return output_url
end
Minify URL using tinyURL:
def minifyUrl(gchartUrl)
# convert URL to escaped one
escaped_url = URI.escape(gchartUrl, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
short_url = open("http://tinyurl.com/api-create.php?url=#{escaped_url}").read
return short_url;
end
Also if you have written a data query method to get start and end time from DB, then it is easy to get startArray and endArray directly by T-SQL. i am using this in practice:
http://snippets.dzone.com/posts/show/3906
or you may take look at this, FYI:
require 'win32ole'
class SQLServer
# This class manages database connection and queries
attr_accessor :connection, :data, :fields
attr_writer :username, :password
def initialize(host, username = 'sa', password='sa')
@connection = nil
@data = nil
@host = host
@username = username
@password = password
end
def open(database)
# Open ADO connection to the SQL Server database
connection_string = "Provider=SQLOLEDB.1;" #SQLOLEDB.1,SQLNCLI
connection_string << "Persist Security Info=False;"
connection_string << "User ID=#{@username};"
connection_string << "password=#{@password};"
connection_string << "Initial Catalog=#{database};"
connection_string << "Data Source=#{@host};"
connection_string << "Network Library=dbmssocn"
@connection = WIN32OLE.new('ADODB.Connection')
@connection.Open(connection_string)
end
def query(sql)
# Create an instance of an ADO Recordset
recordset = WIN32OLE.new('ADODB.Recordset')
# Open the recordset, using an SQL statement and the
# existing ADO connection
recordset.Open(sql, @connection)
# Create and populate an array of field names
@fields = []
recordset.Fields.each do |field|
@fields << field.Name
end
begin
# Move to the first record/row, if any exist
recordset.MoveFirst
# Grab all records
@data = recordset.GetRows
rescue
@data = []
end
recordset.Close
# An ADO Recordset's GetRows method returns an array
# of columns, so we'll use the transpose method to
# convert it to an array of rows
@data = @data.transpose
end
def close
@connection.Close
end
end
Usage sample:
db = SQLServer.new('YourDBIP', 'sa', 'sa')
sp = db.open('DBName')
startArray = db.query("select * from somewhere where ...")
endArray = db.query("sselect * from somewhere where ...")
db.close