package com.meidusa.amoeba.benchmark;
import java.io.File;
import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import com.meidusa.amoeba.config.ConfigUtil;
import com.meidusa.amoeba.log4j.DOMConfigurator;
import com.meidusa.amoeba.net.AuthingableConnection;
import com.meidusa.amoeba.net.BackendConnectionFactory;
import com.meidusa.amoeba.net.Connection;
import com.meidusa.amoeba.net.ConnectionFactory;
import com.meidusa.amoeba.net.ConnectionManager;
import com.meidusa.amoeba.net.ConnectionObserver;
import com.meidusa.amoeba.net.MultiConnectionManagerWrapper;
import com.meidusa.amoeba.util.CmdLineParser;
import com.meidusa.amoeba.util.ObjectMapLoader;
import com.meidusa.amoeba.util.CmdLineParser.BooleanOption;
import com.meidusa.amoeba.util.CmdLineParser.IntegerOption;
import com.meidusa.amoeba.util.CmdLineParser.LongOption;
import com.meidusa.amoeba.util.CmdLineParser.StringOption;
@SuppressWarnings("unchecked")
public abstract class AbstractBenchmark {
private static AbstractBenchmark benckmark;
protected static void setBenchmark(AbstractBenchmark benckmark){
AbstractBenchmark.benckmark = benckmark;
}
private static Properties properties = new Properties();
protected static CmdLineParser parser = new CmdLineParser(System.getProperty("application", "benchmark"));
protected static CmdLineParser.Option debugOption = parser.addOption(new BooleanOption('d', "debug", false,false,true,"show the interaction with the server-side information"));
protected static CmdLineParser.Option portOption = parser.addOption(new IntegerOption('P', "port",true,true,"server port"));
protected static CmdLineParser.Option hostOption = parser.addOption(new StringOption('h', "host",true,true,"127.0.0.1","server host"));
protected static CmdLineParser.Option connOption = parser.addOption(new IntegerOption('c', "conn",true,true,"The number of concurrent connections"));
protected static CmdLineParser.Option totalOption = parser.addOption(new LongOption('n', "total",true,true,"total requests"));
protected static CmdLineParser.Option timeoutOption = parser.addOption(new IntegerOption('t', "timeout",true,false,-1,"query timeout, default value=-1 "));
protected static CmdLineParser.Option contextOption = parser.addOption(new StringOption('C', "context",true,false,"Context xml File"));
protected static CmdLineParser.Option requestOption = parser.addOption(new StringOption('f', "file",true,false,"request xml File"));
protected static CmdLineParser.Option log4jOption = parser.addOption(new StringOption('l', "log4j",true,false,"warn","log4j level[debug,info,warn,error]"));
protected static CmdLineParser.Option helpOption = parser.addOption(new BooleanOption('?', "help",false,false,true,"Show this help message"));
private static Map<String,RandomData> randomMap = new HashMap<String,RandomData>();
private static Map contextMap = new HashMap(){
public Object put(Object key,Object value){
if(value instanceof RandomData){
randomMap.put((String)key, (RandomData)value);
}
super.put(key, value);
return value;
}
};
private List<AbstractBenchmarkClient<?>> benchmarkClientList = new ArrayList<AbstractBenchmarkClient<?>>();
public List<AbstractBenchmarkClient<?>> getBenchmarkClientList() {
return benchmarkClientList;
}
public CmdLineParser getCmdLineParser(){
return parser;
}
public AbstractBenchmark(){
Random random = new Random();
contextMap.put("random",random);
contextMap.put("atomicInteger",new AtomicInteger());
contextMap.put("atomicLong",new AtomicLong());
String requestXml = (String)parser.getOptionValue(requestOption);
if(requestXml != null){
File reqestXmlFile = new File(requestXml);
if(reqestXmlFile.exists() && reqestXmlFile.isFile()){
try {
properties.loadFromXML(new FileInputStream(reqestXmlFile));
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}else{
System.err.println("requestFile not found or is not file :"+reqestXmlFile.getAbsolutePath());
System.exit(-1);
}
}
String contextFile = (String)parser.getOptionValue(contextOption);
if(contextFile != null){
File contextXmlFile = new File(contextFile);
if(contextXmlFile.exists() && contextXmlFile.isFile()){
try {
ObjectMapLoader.load(contextMap,new FileInputStream(contextXmlFile));
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}else{
System.err.println("requestFile not found or not file :"+contextXmlFile.getAbsolutePath());
System.exit(-1);
}
}
}
public abstract ConnectionFactory getConnectionFactory();
private ConnectionManager connManager;
public ConnectionManager getConnManager() {
return connManager;
}
public void setConnManager(ConnectionManager connManager) {
this.connManager = connManager;
}
public Map getNextRequestContextMap(){
Map temp = new HashMap();
temp.putAll(contextMap);
for(Map.Entry<String, RandomData> entry : randomMap.entrySet()){
Object obj = null;
do{
obj = entry.getValue().nextData();
}while(obj == null);
temp.put(entry.getKey(), obj);
}
return temp;
}
public static AbstractBenchmark getInstance(){
return AbstractBenchmark.benckmark;
}
public abstract AbstractBenchmarkClient<?> newBenchmarkClient(Connection conn,CountDownLatch requestLatcher,CountDownLatch responseLatcher,TaskRunnable task);
public static class TaskRunnable{
public boolean running = true;
}
public static void main(String[] args) throws Exception {
String level = (String)parser.getOptionValue(log4jOption);
if(level != null){
System.setProperty("benchmark.level", level);
}else{
System.setProperty("benchmark.level", "warn");
}
final Boolean value = (Boolean)parser.getOptionValue(debugOption,false);
String log4jConf = System.getProperty("log4j.conf","${amoeba.home}/conf/log4j.xml");
log4jConf = ConfigUtil.filter(log4jConf);
File logconf = new File(log4jConf);
if(logconf.exists() && logconf.isFile()){
DOMConfigurator.configureAndWatch(logconf.getAbsolutePath(), System.getProperties());
}
int conn = (Integer)parser.getOptionValue(connOption);
final long total = (Long)parser.getOptionValue(totalOption);
String ip = parser.getOptionValue(hostOption).toString();
final CountDownLatch requestLatcher = new CountDownLatch((int)total);
final CountDownLatch responseLatcher = new CountDownLatch((int)total);
final TaskRunnable task = new TaskRunnable();
final AtomicLong errorNum = new AtomicLong(0);
int port = (Integer)parser.getOptionValue(portOption);
final MultiConnectionManagerWrapper manager = new MultiConnectionManagerWrapper();
manager.addConnectionObserver(new ConnectionObserver(){
@Override
public void connectionClosed(Connection conn) {
if(value.booleanValue()){
System.out.println(new Date() +" client conn="+conn.getSocketId()+" closed!");
}
}
@Override
public void connectionEstablished(Connection conn) {
if(value.booleanValue()){
System.out.println(new Date() +" client conn="+conn.getSocketId()+" connected!");
}
}
@Override
public void connectionFailed(Connection conn, Exception fault) {
if(value.booleanValue()){
System.out.println(new Date() +" client conn="+conn.getSocketId()+ " faild!! "+(fault!= null? (" fault="+fault.getMessage()):""));
}
if(conn instanceof AuthingableConnection){
AuthingableConnection authConn = (AuthingableConnection)conn;
if(authConn.isAuthenticatedSeted() && authConn.isAuthenticated()){
errorNum.incrementAndGet();
}
}else{
errorNum.incrementAndGet();
}
}
});
Integer timeout = (Integer)parser.getOptionValue(timeoutOption,-1);
if(timeout >0){
manager.setIdleCheckTime(timeout);
}
manager.init();
manager.start();
Thread.sleep(100L);
System.out.println("Connection manager started....");
new Thread(){
long lastCount = responseLatcher.getCount();
long lastTime = System.currentTimeMillis();
{this.setDaemon(true);}
public void run(){
while(responseLatcher.getCount()>0){
long current = responseLatcher.getCount();
long currentTime = System.currentTimeMillis();
long tps = 0;
if(currentTime > lastTime){
tps = (lastCount - current) * 1000 /(currentTime-lastTime);
}else{
tps = (lastCount - current);
}
lastCount = current;
lastTime = currentTime;
System.out.println(new Date() +" compeleted="+(total - lastCount)+ " TPS="+tps +" ,conns="+manager.getSize());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
if(requestLatcher.getCount() == 0){
if( responseLatcher.getCount() - errorNum.get() <=0){
break;
}
}
}
for(long i=0;i<errorNum.get();i++){
responseLatcher.countDown();
}
System.out.println(new Date() +" compeleted="+(total));
}
}.start();
System.out.println("\r\nconnect to ip="+ip+",port="+port+",connection size="+conn+",total request="+total);
AbstractBenchmark benckmark = AbstractBenchmark.getInstance();
benckmark.setConnManager(manager);
ConnectionFactory factory = benckmark.getConnectionFactory();
if(factory instanceof BackendConnectionFactory)
{
((BackendConnectionFactory)factory).setManager(manager);
((BackendConnectionFactory)factory).setIpAddress(ip);
((BackendConnectionFactory)factory).setPort(port);
((BackendConnectionFactory)factory).init();
}
long createConnectionStartTime = System.nanoTime();
for(int i=0;i<conn;i++){
InetSocketAddress address = new InetSocketAddress(ip,port);
try{
Connection connection = factory.createConnection(SocketChannel.open(address),System.currentTimeMillis());
AbstractBenchmarkClient<?> client = benckmark.newBenchmarkClient(connection,requestLatcher,responseLatcher,task);
client.setBenchmark(benckmark);
client.setTimeout(timeout.intValue());
client.setDebug(value.booleanValue());
client.putAllRequestProperties(properties);
client.init();
if(!(factory instanceof BackendConnectionFactory)){
manager.postRegisterNetEventHandler(client.getConnection(), SelectionKey.OP_READ);
}
benckmark.benchmarkClientList.add(client);
}catch(Exception e){
System.err.println("connect to "+address+" error:");
e.printStackTrace();
System.exit(-1);
}
}
long createConnectionEndTime = System.nanoTime();
for(AbstractBenchmarkClient<?> connection: benckmark.benchmarkClientList){
if(requestLatcher.getCount()>0){
requestLatcher.countDown();
connection.startBenchmark();
}
}
requestLatcher.await();
task.running = false;
responseLatcher.await();
long endBenchmarkTime = System.nanoTime();
long min = benckmark.benchmarkClientList.get(0).min;
long max = 0;
long minStart = benckmark.benchmarkClientList.get(0).start;
long maxend = 0;
long average = 0;
int totleConnection = 0;
for(AbstractBenchmarkClient<?> connection: benckmark.benchmarkClientList){
if(connection.count>0){
min = Math.min(min, connection.min);
max = Math.max(max, connection.max);
average += (connection.end - connection.start)/connection.count;
minStart = Math.min(minStart,connection.start);
maxend = Math.max(maxend, connection.end);
totleConnection ++;
}
}
long time = TimeUnit.MILLISECONDS.convert((maxend - minStart),TimeUnit.NANOSECONDS);
System.out.println("completed requests total="+total+ ", errorNum="+errorNum.get()+", cost="+TimeUnit.MILLISECONDS.convert((maxend - minStart), TimeUnit.NANOSECONDS)+"ms , TPS="+ (time>0?((long)total*1000)/time:total)+"/s");
System.out.println("min="+TimeUnit.MILLISECONDS.convert(min, TimeUnit.NANOSECONDS)+"ms");
System.out.println("max="+TimeUnit.MILLISECONDS.convert(max, TimeUnit.NANOSECONDS)+"ms");
average = TimeUnit.MILLISECONDS.convert(average, TimeUnit.NANOSECONDS)/totleConnection;
System.out.println("average="+average+"ms");
System.out.println("create Connections time="+TimeUnit.MILLISECONDS.convert(createConnectionEndTime - createConnectionStartTime, TimeUnit.NANOSECONDS)+"ms");
long tpsTime = TimeUnit.MILLISECONDS.convert(endBenchmarkTime - createConnectionEndTime, TimeUnit.NANOSECONDS);
System.out.println("TPS(after connected)="+(tpsTime>0?((long)total*1000)/tpsTime:total)+"/s");
manager.shutdown();
}
}