用同一个Java Socket Client 分别调用用BIO和NIO实现的Socket Server, 观察其建立一个Socket (TCP Connection)所需要的时间,从而计算出Server吞吐量TPS。
之所以可以用Connection建立时间来计算TPS,而不考虑业务逻辑运行时间,是因为这里的业务逻辑很简单,只是Echo回从client传过来的字符,所消耗时间可以忽略不计。
注意: 在现实场景中,业务逻辑会比较复杂,TPS的计算必须综合考虑IO时间+业务逻辑执行时间+多线程并行运行情况 等因素的影响。
1. Java Socket Client
public class PlainEchoClient {
public static void main(String args[]) throws Exception {
for (int i = 0; i < 20; i++) {
startClientThread();
}
}
private static void startClientThread() throws UnknownHostException,
IOException {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
startClient();
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.start();
}
private static void startClient() throws UnknownHostException, IOException {
long beforeTime = System.nanoTime();
String host = "127.0.0.1";
int port = 8086;
Socket client = new Socket(host, port);
// 建立连接后就可以往服务端写数据了
Writer writer = new OutputStreamWriter(client.getOutputStream());
writer.write("Hello Server.");
writer.flush();
// 写完以后进行读操作
Reader reader = new InputStreamReader(client.getInputStream());
char chars[] = new char[64];// 假设所接收字符不超过64位,just for demo
int len = reader.read(chars);
StringBuffer sb = new StringBuffer();
sb.append(new String(chars, 0, len));
System.out.println("From server: " + sb);
writer.close();
reader.close();
client.close();
System.out.println("Client use time: "
+ (System.nanoTime() - beforeTime) + " ns");
}
}
2. IO Socket Server
这个Socket Server模拟的是我们经常使用的thread-per-connection模式, Tomcat,JBoss等Web Container都是这种方式。
public class PlainEchoServer {
private static final ExecutorService executorPool = Executors.newFixedThreadPool(5);
private static class Handler implements Runnable{
private Socket clientSocket;
public Handler(Socket clientSocket){
this.clientSocket = clientSocket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(
clientSocket.getOutputStream(), true);
char chars[] = new char[64];
int len = reader.read(chars);
StringBuffer sb = new StringBuffer();
sb.append(new String(chars, 0, len));
System.out.println("From client: " + sb);
writer.write(sb.toString());
writer.flush();
} catch (IOException e) {
e.printStackTrace();
try {
clientSocket.close();
} catch (IOException ex) {
// ignore on close
}
}
}
}
public void serve(int port) throws IOException {
final ServerSocket socket = new ServerSocket(port);
try {
while (true) {
long beforeTime = System.nanoTime();
final Socket clientSocket = socket.accept();
System.out.println("Establish connection time: "+ (System.nanoTime()-beforeTime)+" ns");
executorPool.execute(new Handler(clientSocket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException{
PlainEchoServer server = new PlainEchoServer();
server.serve(8086);
}
}
public class PlainNioEchoServer {
public void serve(int port) throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address); // #1
serverChannel.configureBlocking(false);
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // #2
while (true) {
try {
selector.select(); // #3
} catch (IOException ex) {
ex.printStackTrace();
// handle in a proper way
break;
}
Set readyKeys = selector.selectedKeys(); // #4
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key
.channel();
long beforeTime = System.nanoTime();
SocketChannel client = server.accept(); // #6
System.out.println("Accept connection time: "+ (System.nanoTime()-beforeTime)+" ns");
if (client == null){//Check if socketChannel has been created, it could be null, because it's non-blocking
continue;
}
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_WRITE
| SelectionKey.OP_READ,
ByteBuffer.allocate(100));
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
client.read(output);
}
if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
output.flip();
client.write(output);
output.compact();
}
} catch (IOException ex) {
key.cancel();
try {
key.channel().close();
} catch (IOException cex) {
}
}
iterator.remove(); // #5
}
}
}
public static void main(String[] args) throws IOException{
PlainNioEchoServer server = new PlainNioEchoServer();
server.serve(8086);
}
}