Writing a multi-process network server in Java with Akuma
As I mentioned in my last post, Akuma does fork/exec by itself.
One of the things that this enables, other than daemonization, is an ability to inherit file descriptors to child processes. This is a very much normal part of life for C programmers, but presumably because Windows don't have fork (even though it does have the notion of file descriptor inheritance), Java doesn't let you do this.
There are several interesting use cases for this, for example to start a new child process with the same stdin/stdout as your program (without the need of pumping the stdout/stderr by using separate threads, which can introduce ordering problem between stdout and stderr.)
But more interestingly (for me anyway), this can be used to write a multi-process network server like Apache, where multiple processes listen to the same TCP port. This is done by the following steps:
- Open a ServerSocket
- Fork a bunch of worker processes (they inherit the server socket.)
- Worker processes accept connections on the server socket.
In this way, even when one of the worker process die, clients will not see any service interruption. Got some memory leak problem? No problem — just kill a worker process every once in a while. No need to waste your time debugging the cause of the problem.
Yes, clustered appliation servers would achieve the same thing, but this is much less intrusive — users won't have to set up separate TCP/IP sockets for each of the instances, for example. This should also run faster, as it's now the kernel that acts as the load balancer, not an user-space program.
And further more, if you combine this with exec(2), I think you can get rid of the dynamic reconfiguration of an application server altogether — again, just like Apache, if you need to reload configuration, just kill off worker processes (after they finished serving their current requests), and have them exec to themselves. Writing mutable classes that work correctly in the face of concurrency is really tricky. If we don't need to do that, and if that simplification contributes to the smaller/faster codebase, perhaps it works out (even if individual worker process takes longer to restart, compared to Apache.)
Presumably this is all basically just rediscovering what Unix has been doing for a long time, but still, the fact that we can now do this in Java excites me a lot.