import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
class SelectAttachment {
private static final Random r = new Random();
// private static final String[] urls = { "/d/aarp", "/d/about", "/d/zoo",
// "/d/throw",
// "/d/new", "/tmpls.js", "/mustache.js" };
private static final String[] urls = { "/", "/css/landing.css", "/imgs/l/scott.png" };
String uri;
ByteBuffer request;
int response_length = -1;
int response_cnt = -1;
public SelectAttachment(String uri) {
this.uri = uri;
request = ByteBuffer.wrap(("GET " + uri + " HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")
.getBytes());
}
public static SelectAttachment next() {
return new SelectAttachment(urls[r.nextInt(urls.length)]);
}
}
public class PerformanceBench {
static final boolean DEBUG = false;
static final byte CR = 13;
static final byte LF = 10;
static final String CL = "content-length: ";
public static String readLine(ByteBuffer buffer) {
StringBuilder sb = new StringBuilder(64);
char b;
loop: for (;;) {
b = (char) buffer.get();
switch (b) {
case CR:
if (buffer.get() == LF)
break loop;
break;
case LF:
break loop;
}
sb.append(b);
}
return sb.toString();
}
private static void D(String mesg) {
if (DEBUG) {
System.out.println(mesg);
}
}
final static int concurrency = 4000;
final static int total = 2000000;
static int remaining = total;
static long totalByteReceive = 0;
static InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 4348);
static ByteBuffer readBuffer = ByteBuffer.allocateDirect(1024 * 64);
static long start = System.currentTimeMillis();
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("concurrency: " + concurrency + "; total requests: " + total);
Selector selector = Selector.open();
for (int i = 0; i < concurrency; ++i) {
if (i % 100 == 0) {
Thread.sleep(10);
}
connect(addr, selector);
}
start = System.currentTimeMillis();
while (true) {
int select = selector.select();
D("selec return " + select);
if (select > 0) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (key.isConnectable()) {
SocketChannel ch = (SocketChannel) key.channel();
try {
if (ch.finishConnect()) {
key.interestOps(SelectionKey.OP_WRITE);
}
} catch (Exception e) {
ch.close();
e.printStackTrace();
D(e.getMessage() + "\t" + "close and reconnect");
connect(addr, selector); // reconnect
}
} else if (key.isWritable()) {
doWrite(key);
} else if (key.isReadable()) {
doRead(selector, key);
}
}
selectedKeys.clear();
}
}
}
private static void doWrite(SelectionKey key) throws IOException {
SocketChannel ch = (SocketChannel) key.channel();
SelectAttachment att = (SelectAttachment) key.attachment();
ByteBuffer buffer = att.request;
ch.write(buffer);
if (!buffer.hasRemaining()) {
key.interestOps(SelectionKey.OP_READ);
}
}
private static void doRead(Selector selector, SelectionKey key) throws IOException,
SocketException, ClosedChannelException {
SocketChannel ch = (SocketChannel) key.channel();
SelectAttachment att = (SelectAttachment) key.attachment();
readBuffer.clear();
while (true) {
int read = ch.read(readBuffer);
if (read == -1) {
ch.close();
decAndPrint();
D("remote closed, connecton new, remaining: " + remaining);
connect(addr, selector);
break;
} else if (read == 0) {
D("read zero");
break;
} else {
totalByteReceive += read;
if (att.response_length == -1) {
readBuffer.flip();
String line = readLine(readBuffer);
while (line.length() > 0) {
line = line.toLowerCase();
if (line.startsWith(CL)) {
String length = line.substring(CL.length());
att.response_length = Integer.valueOf(length);
att.response_cnt = att.response_length;
}
line = readLine(readBuffer);
}
att.response_cnt -= readBuffer.remaining();
} else {
att.response_cnt -= read;
}
if (att.response_cnt == 0) {
D("read all");
decAndPrint();
key.attach(SelectAttachment.next());
key.interestOps(SelectionKey.OP_WRITE);
}
}
}
}
private static void decAndPrint() {
remaining -= 1;
if (remaining % (total / 10) == 0) {
System.out.println("remaining\t" + remaining);
}
if (remaining == 0) {
long time = (System.currentTimeMillis() - start);
double receiveM = totalByteReceive / 1024.0 / 1024;
double reqs = (double) total / time * 1000;
double ms = (double) receiveM / time * 1000;
System.out
.printf("concurrency: %d; requests: %d; time: %dms; req/s: %.2f; receive bytes: %.2fM data; throughput: %.2f M/s\n",
concurrency, total, time, reqs, receiveM, ms);
System.exit(0);
}
}
private static void connect(InetSocketAddress addr, Selector selector) throws IOException,
SocketException, ClosedChannelException {
SocketChannel ch = SocketChannel.open();
ch.socket().setReuseAddress(true);
ch.configureBlocking(false);
ch.register(selector, SelectionKey.OP_CONNECT, SelectAttachment.next());
ch.connect(addr);
}
}