Hey guys, I'm working on a server program that is meant to scale well and serve potentially thousands of clients. The thing is, I feel that Apache MINA is too heavyweight so I decided to not use it and wrote my own client listener instead. I never really performed asynchronous socket operations in Java (C# made that so much easier, but I really preferred to write this project in Java since I'm more familiar with it in everything besides socket reads), so trying to understand how to use the thread pool correctly is hard for me. I used Apache MINA documentation to get an idea of how things should be done. I got two questions:
- Is the thread pool used correctly? Apache MINA's default thread size is the number of CPU cores + 1, but should I really use a 3 thread thread pool for my Core 2 Duo in order to accept thousands of clients?
- I know that reallocating the buffer twice for each message received from the client (each message is two packets, one header that is a constant 4 bytes and a content packet that has its length specified in the header). Is there an easy way to use a fixed size buffer that checks for buffer overruns so that behavior is still the same but the buffer doesn't have to be constantly reallocated?
Here's how I start the listener:
ClientListener cl = new ClientListener(1234);
cl.init();
new Thread(cl).start();
Here is the relevant code for ClientListener:
private static final int THREADS = Runtime.getRuntime().availableProcessors() + 1;
private ServerSocket socket;
private ExecutorService threadPool;
private int port;
public ClientListener(int port) {
this.port = port;
threadPool = Executors.newFixedThreadPool(THREADS);
}
public void init() {
try {
socket = new ServerSocket(port);
} catch (IOException ex) {
}
}
public void run() {
while (true) {
try {
ClientSession s = new ClientSession(socket.accept());
threadPool.execute(s);
} catch (IOException ex) {
}
}
}
ClientSession relevant code:
private Socket socket;
private byte[] buffer;
private boolean isHeader;
public ClientSession(Socket socket) {
this.socket = socket;
this.buffer = new byte[4];
this.isHeader = true;
}
public void run() {
InputStream in;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (IOException ex) {
return;
}
while (!socket.isClosed()) {
try {
int read = in.read(buffer);
if (read == -1)
break;
receive(read);
} catch (IOException ex) {
break;
}
}
}
private void receive(int readBytes) {
if (isHeader) {
if (readBytes >= 4) {
buffer = new byte[getPacketLength(buffer)];
isHeader = false;
} else {
System.out.println("Not enough data received from client " + socket.getInetAddress() + " to decode 开发者_JAVA技巧packet.");
}
} else {
if (readBytes >= buffer.length) {
processMessage(new LittleEndianByteArrayReader(decryptData(buffer)), this);
buffer = new byte[4];
isHeader = true;
} else {
System.out.println("Not enough data received from client " + socket.getInetAddress() + " to decode packet (needed " + buffer.length + ", received " + readBytes + ").");
}
}
}
You don't need to know the code for getPacketLength, processMessage, decryptData, and the class LittleEndianByteArrayReader, but I'm pretty sure the purposes of those methods/classes are obvious.
The number of threads in blocking IO scenario have to be calculated by the number of clients and the time each client connection will be open. Each connection of each user requires on thread.
With only three threads a user could simply block your server until connection timeout by just opening three TCP connections and not sending any data to your server.
Nevermind guys. I realized that Apache MINA actually uses NIO which is why I got confused. It really needs only one thread to process requests with the use of selectors. Thanks for all your answers and sorry about the confusion!
精彩评论