Advertisement

How would you scale single-threaded server?

Started by May 26, 2016 09:55 PM
6 comments, last by Kylotan 8 years, 5 months ago

Hi,

Hmm.. I am currently going through different designs of game server back-ends and it gets me wonder: how would you scale single-threaded server?

What comes to my mind atm. is that if the server is made using back-end like node.js would it be possible to start e.g. 4 node.js processes which all are executing the same game server program? (to benefit from amount of cores available).

I noticed that this is called "clustering" in node.js or something.

Nevertheless, the problem which troubles me is the fact that I think (?) I cannot control which of these processes are getting the requests and I would like all of the players from 4 different processes to be able to interact in a same virtual world through websockets. That leads to a question that has anyone done anything similar?

Hence, I had also an idea of using redis as an intermediate to store the game state, so all processes can read/write to it:

redis

process 1 -- | -- process 2

process 3 -- | -- process 4

But that would still mean that each of these processes has their own game loop and it confuses me concerning the interactions? when player i and j stands next to each other they could come from process 1 and 2... can they even collide like that? :wacko: any ideas? thus, couldn't find any material of using multiple server-side game loops like this.

-

You will realistically either need to "shard" (that is, run separate worlds on each server) or assign different locations (geographic areas, dungeons, instances, whatever) to different servers ("zone").

In every other case, it gets... troublesome. Not like managing zones isn't already troublesome, but it's nothing compared to the trouble of several servers managing the same zone.

(Note: I use the words "server" and "process" as synonyms.)

As for redis to communicate... this "works", but be aware that your server processes will necessarily run lockstep like this (so there is not an awful lot of scaling, the only thing you gain is sending data to clients in parallel, which is not much). Redis runs serially on one process, so while e.g. server 1 is writing to the database and servers 2 and 3 are trying to read, they will be blocked. Basically, you get the performance of only a single server, more or less. Probably less, if you count in the overhead by going through redis, with network and parsing, and all.

For zones, you usually set up a network of several layers where the nodes that clients connect to subscribe to publishers (which run the actual game in a zone), plus some dedicated database servers, plus some for authenticating, etc etc. This scales well and it is kinda robust, too. Pub-sub works the same with 3 servers, or with 2,500 servers... which is a big plus.

You will probably need to explain in more detail what exactly is expected from your game (how many uswrs, how realtime, what interaction, etc).
Advertisement

how would you scale single-threaded server?


You run one process per core on the hosting server, and send different users to different processes.
Typically, each process will listen on a different port, and you'll use some kind of registry/discovery to manage it all.
enum Bool { True, False, FileNotFound };

Thanks for the replies, they gave me some ideas to try out. For now I will try to go with an approach that they are only one game loop -process per computer, and features which can be run independently will be run at different processes.

I also looked into making processes to listen on different ports, and I have a feeling that it is possible (even though all the examples shows how to listen same ports).

After these changes, I think I have something like the following:

One server has:

- Game process (basically, the game loop stuff - fast actions - movement/attacking/NPCs etc.)

- Chat process (doesn't need to be that fast, but needs mostly likely AOI to filter sent messages)

- Template process (responsible of returning e.g. data for the dialogs and alike.. when you open a shop?).

- Anything else?

Hmm.. the point with this approach is the fact that single-threaded server doesn't like sending of big responses like templates.. but also that I can use separate websocket connection from client to game and chat process. (chat connection can be then closed by the user if wanted etc.)

And yes, I guess I still need a one server which knows all the game servers in the system so that player can be connected them appropriately.

-

Do the shop templates have to be big? Server can send a message that contains SHOW_SHOP_INVENTORY followed by a number of how much items, and followed by N pairs of quantity and item-ID. Everything else (the names, the dialog, etc) is invariant stuff, can be stores on the client.

Do the shop templates have to be big? Server can send a message that contains SHOW_SHOP_INVENTORY followed by a number of how much items, and followed by N pairs of quantity and item-ID. Everything else (the names, the dialog, etc) is invariant stuff, can be stores on the client.

Yea, I think you have a point there. Then I could add just more logic to the client-side that it is able to place that information into these dialogs which are generated with js or picked directly from the DOM. That being said, I would probably still make the separate template server because if I build it using react or something I would minimize the risk of doing javascript memore leaks which may occur with these kind of SPAs.. so there are some trade offs which needs to be taken into consideration.

-

Advertisement
Take a look at how irc networks work.

The majority of irc daemons are single threaded and the networks handle hundreds of thousands of concurrent connections by linking servers in an acyclic spanning tree with the servers geographically separate.

When I last attempted an online game I used my irc server code as a basis of the backbone for it for the reason of it being stable and tried and tested although high latency. It's worth looking into how others solved problems, even outside of the games industry :)

Take a look at how irc networks work.


I agree that it's an interesting systems design example, and looking at how it grew from the first implementation in the 80s is another interesting case study.
But IRC is not a good architecture for games, for the same reason you said: latency is not a goal there.

It's worth looking into how others solved problems, even outside of the games industry


This is also a reasonable statement, but with some caveats. A lot of companies and individuals in other industries have over the years come in, and thought "we're the best in our industry at doing X, and games seem like they do a lot of X, so our solution will totally rule games!"
That seldom ends well. Doesn't matter if it's physical simulation, networking, visualization, stock trading, media serving, or whatever -- the evolutionary pressures in the game industry are very different from the evolutionary pressures in most other industries.
enum Bool { True, False, FileNotFound };

It's worth looking into how others solved problems, even outside of the games industry :)

Trust me when I say that I have, I have been googling all over the place and tried to fit my web development knowledge to game development as much as I can :rolleyes:.. there are just infinite amount of things to learn when it comes to making mogs - maybe that is the reason why it is so interesting. :cool:

-

Coming into this late, but I would address this in one of two ways depending on the game's needs.

1) Keep the single server for network traffic and run other processes for other tasks (eg. AI, database reads/writes, account management/authentication).

2) Use several of these servers, but just as a proxy that forwards messages to and from the main, multi-threaded server. This is probably only sensible if your single-threaded server gives you significant benefits in some other way (eg. ease of use, cost)

It seems to me that Node.js and websockets are not a great mix since the former is optimised for event-based behaviour and the latter is optimised for long-running data streams. If you're willing to consider something other than JavaScript then you might consider abandoning it at this point, and picking something that gives you more multi-threading options.

This topic is closed to new replies.

Advertisement