Non-blocking server loop in Node.js with process.nextTick()
Posted: Wed Nov 21, 2012 4:14 am
Some of you may remember I started a conversation a while back wondering how you would do background-processing for a game server made in Node.js. In traditional multithreaded game servers, you actually create a literal loop with something like WHILE(true) so that the server loops through all the tasks it has to do again and again. Of course, since node is single-threaded, you cannot do this or you will block the event loop and break the whole application. Well, I finally wrapped my head around process.nextTick(), and it seems like a great solution for many circumstances (see my note below).
Basically, process.nextTick() defers execution of the function given as its argument until the next "tick", or loop through of the master event loop in node. If you have CPU-intensive code to run, or need something run repetitively (such is the case with a game server), you can set up an event loop-friendly server loop like this:
If instead of using process.nextTick() on the last line of the function you had simply called serverMainLoop() directly, this would have caused an infinite loop, and no input, output or any other events would occur in your application due to "CPU starvation". Essentially the serverMainLoop() function would take the single thread of the server and never give it back.
But by instead deferring the next run of the serverMainLoop() function until the next tick of the server, this means that the serverMainLoop() function will not run again until all other events that are queued-up in the event loop have occurred. This means that websocket communications, file I/O, database communication, etc. all get their chance to run before the server loop starts up again.
However, this is not perfect for all situations. If you have a particularly long server loop with tons of processing, you may be better off with real child processes. Also, this method would probably work best by keeping the majority of the game world state in-memory, and simply set up a JS timer to commit it to the database every few minutes.
Hope this helps for anyone out there wanting to use node for an MMORPG or MUD server but who had a hard time grappling with asynchronous programming and the event loop model of node.
Basically, process.nextTick() defers execution of the function given as its argument until the next "tick", or loop through of the master event loop in node. If you have CPU-intensive code to run, or need something run repetitively (such is the case with a game server), you can set up an event loop-friendly server loop like this:
Code: Select all
function serverMainLoop() {
//do all processing needed for background activity.
//this includes moving around mobs, resolving monster AI
//changing from day to night, and anything else that needs
//to be done but not in response to a player-initiated event
process.nextTick(serverMainLoop);
}
serverMainLoop();
But by instead deferring the next run of the serverMainLoop() function until the next tick of the server, this means that the serverMainLoop() function will not run again until all other events that are queued-up in the event loop have occurred. This means that websocket communications, file I/O, database communication, etc. all get their chance to run before the server loop starts up again.
However, this is not perfect for all situations. If you have a particularly long server loop with tons of processing, you may be better off with real child processes. Also, this method would probably work best by keeping the majority of the game world state in-memory, and simply set up a JS timer to commit it to the database every few minutes.
Hope this helps for anyone out there wanting to use node for an MMORPG or MUD server but who had a hard time grappling with asynchronous programming and the event loop model of node.