Hello! I am new to this board and dang, I wish I knew about it a year ago! I suspect I will be back often
I saw this thread and wanted to speak up a bit. In Java 1.4+ it is fully possible to create a very powerful and scalable server. Java NBIO is more then capable but the documentation is simply terrible. Many of the examples of NBIO online are very poor to say the least. Also, there were several cross-platform JDK bugs that are just now resolved (I believe). Personally, I found NBIO to be quite confusing as well but that might be just me.
With much help from Ron Hitchens (author of the O'Reilly Java NBIO book) our chat server has been load tested to 5000 concurrent connections on Windows 2003 Server. At that point, the OS simply started blocking all inbound connections (never really figured out why either).
After three unique versions of the server, the key learnings for a high performance server are pretty simple and really boil down to these two things:
1. You need to use NIO to managed all the incoming and outgoing data. We use a single thread to constantly poll everyone for incoming messages. Every connected client has a buffer they store partially completed messages in that is drained when a message terminator is received.
2. Use a thread pool to handle dealing with every transaction from the clients. If you think about it, each message from a client is a discrete unit of work. To that end, our server has a mapping of all types of transactions it handles. We take a message from the client and a worker thread then determines which handler users it, executes the handler, disposes of the message, and returns to the pool.
We have played with several other threading models from 2 threads per user (one for inbound data and one for outbound data) to a thread per room. Thread per room works pretty well, but can lag pretty bad when you get into lots of messages in a room. Private messaging of users cross rooms gets really messy too. The two threads per user option is by FAR the simplest but also the least scalable. It seems to crap out at about 300 or so users and the memory usage gets pretty extreme.
Be extremely careful with synchronization. This is by far the biggest problem we have. We are STILL squashing synchronization bugs where we get a deadlock on a single thread in the pool and things become unstable. It's very hard to isolate.
To handle room level messaging, you simply need to ensure the Room on the server knows all Clients inside of it. Then the room has a method like "sendMessageToRoom()" to iterate over the clients and send a specified message to them.
To handle private messaging, we use the username as a key in a HashMap at the chat server level. If you want to send a message to someone, you simply ask the server to look up the Client object from the HashMap and use the sendMessage() method on the client. This seems to work pretty well. The downside of the using a username as a key is that you have to "log in" first. The user doesn't have a username until after it connects AND logs in. This means that if you need to reference or message a user before they logged in, you have to provide another means for locating them. We handle this by requiring that login be the very first transaction called before all others. Anyone that breaks this rule is disconnected immediately. We also have a clean-up thread that disconnects people that have connected but not logged in after a configurable period.
Thus far everything described is completely protocol agnostic. Everything we do uses TCP entirely. A single channel between client and server. I suppose we could use a second channel via UDP or some such like many games but thus far it hasn't been much of an issue and so we haven't explored it too far.
To make it worse, we actually use XML as the format for all messages. I'm sure some of you are cringing, but hear me out. We have a very high performance and lightweight (interpret this as VERY SIMPLE
) parser that we use on the server. This has proven to work great and be nice and simple for clients in multiple programming languages. In the next release of the server, we will support replacing this with a message interpreter of chose based on an interface. This will allow people to use more effecient message formats at the expense of simplicity.
bmyers mentioned the idea of using JMS. We found this intriguing and played around with it about a year ago. We used dynamic topics for rooms and it works pretty well but the machinery to handle keeping track of who and what was where became quite a chore. In the end, it proved overly complicated and performance heavy due to all the J2EE code involved. BUT, this might be a perfect solution depending on the requirements of a given game. One thing to keep in mind is that if this is an application on their machine or a signed applet, you can actually run the JMS server on each users machine to provide direct access between users in a P2P style configuration...
Woah! I just looked back at what I wrote and realized this this has become a rather lengthy tome
! Sorry about that, I hope this was helpful to someone! Feel free to contact me with any questions.