The server is essentially a queue of items, while the clients act as producers and/or consumers of such items.
The server must:
HashMap.get;PriorityQueue.poll or PriorityQueue.offer;The easiest way of setting this up, is by having a thread accepting clients, and then creating two threads for each client:
InputStream, which blocks waiting for requests;OutputStream, which listens for events on the queue, and sends the information to the client.Surely this isn't scalable, and it seems wasteful to have two threads for each client.
I also thought about using a single thread, which would
read of about 1 second;read times out, or after processing the request;However, polling for requests and events is also wasteful.
Another approach would use a thread pool, and put each of the two above actions in their respective Runnable. These runnables would then queue each other in the Executor.
This seems just as wasteful, if not more.
I've been reading some questions, and I'm now curious about NIO, since non-blocking operations and an event-driven server seems to be the right approach.
Is any of the above designs suitable for this task, or should I tackle it with NIO?
In terms of numbers, this is more of an exercise than a real system, so it doesn't have to deal with thousands of clients, but, ideally, it should be able to perform and scale well.
Well you need to keep using the ThreadPool because you need to manage Runners!, I suggest you impl MOA structure in your business, as this client connect to the server and server waits for the client request(data), then server queue the job(if there is no thread available to process) and imediatly response an long value which points to the client process id at the server and close the socket. now what if clients request has processed and is ready for action? so here there is two approach, the good one is that server signal the client(so client needs to listen about the server response[ServerSocket]) about the finished request. OR client checks the server in some regular intervals and checks the status of the process.
Two threads per client is definitely not scalable.
If you have M cores on the server, you can't actually do better than having M threads running. Anything higher exposes you to thrashing, and will reduce the number of executed operations per second.
A better design partitions the available cores into U updaters and L listeners, where U + L == M.
Each client is assigned (ideally with load balancing, but that's an embellishment) to an updater thread. Each update event is multicast to all the updater threads, which each then update all their assigned clients. A client at the end of an updater's list is updated later than one at the beginning, but there is no help for it: you only have so much hardware.
Similarly, each client is assigned to a listener thread, which handles more than one listener. Client input is dumped into a FIFO queue, and processed by the listener thread as soon as it gets around to it.
Each thread can then stay active and in memory, while client data is moved through the system. The design degrades gracefully, in that too many clients means all updates get slow as a linear function of the number of clients. The designs you propose will degrade faster than that.
Modern (e.g., later than, say 2002) web servers bury this all deep in the implementation, so developers don't need to manage it. But it's still a useful exercise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With