Multithreading message queue ... performance concerns?

Started by
19 comments, last by Kylotan 7 years, 3 months ago

Thank you guys for the input.

I was reading into the whole NIO stuff again for the past few days. I even got it working (to some extent) with the game client. But i have to say, it's hard.

(Compared to the thread per client approach.)

So i managed to merge the non-TCP player threads into one single thread which iterates over all registered players and sends data back and/or processes other stuff (validating coordinates, etc...).

Now the TCP side...

I also merged all TCP threads (thread per socket) into one NIO thread, but it gets complicated very fast.

First, the thread is reading the data from the sockets which then has to be temporarily buffered in a seperate bytebuffer. (As i could either recieve only a fragment of a full message or multiple messages at once.)

Then i have to loop over this buffer and parse the data in order to find out if one full message was actually recieved and is stored in that buffer.

Then i have to copy that message out, create a message object, put that byte data into the message object and send it to the other thread which then responds/processes this message.

The bytes in the temporary message buffer then need to be shifted (to fill the gap where the full message was "cut out") in order to avoid breaking the parsing process.

All that stuff seams kinda heavy weight for a single thread which needs to handle up to 64 connections at once. (Especially the whole parsing, and message creating process.)

My idea was to create a seperate thread which does the parsing/message creation process alongside the TCP listener thread in order to split that load on 2 cores, but then i need additional locks to avoid any kind of race-conditions or errors due to the message buffer which would be shared alongside two threads. (And i have no idea if the locks would make the situation even worse if they interfere with each other too much.)

I have no idea how to proceed further. (I'm really considering ditching that.)

There are 3 options now:

a) try to proceed further with the NIO stuff (which is kinda complicated and in addition to that i really don't have any clue if the general server architecture which i'm building is that much better compared to the thread per client stuff. > Lack of experience and knowledge on my part.)

b) Use the thread per socket model but merge the other non-TCP threads into one single thread which iterates over all registered clients. (The server would then have a maximum of 64 threads for each socket + 3 to 4 other threads for all the other systems which are running in the background.)

c) Revert back to the original architecture (1 reader and 1 processing/writer thread per client. Total number of threads would be 64*2= 128 + 3 to 4 other threads.)

It would be a good idea to benachmark that stuff. (But i have really no clue how. I never wrote any kind of servers before nor bencharked a server application. Do i have to write a software which emulates 64 clients? Is this the common way of doing things?)

Advertisement

Do i have to write a software which emulates 64 clients? Is this the common way of doing things?


Yes, and yes.

If your regular game client can run without graphics/sound/input, from a command line, with a script controlling it, then you may be able to run many instances of your regular client from the command line on the same machine, and get a few machines to get to the number of simultaneous clients you need.
enum Bool { True, False, FileNotFound };

Most of the stuff that sounds awkward there would exist whether I/O is being handled in threads or not. So perhaps you are overestimating the complexity.

Message parsing can be simplified if your messages start with their size. Then, knowing if you have a full message or not can be as easy as comparing the buffer size with the size value at or near the start of the buffer. Only when you have a full message do you then have to worry about parsing it, but you can extract the message data and hand it off to another thread for parsing if you like. Don't forget to check that the supplied size value is reasonable...

You can also avoid shifting any data after you extract a message if you use a ring buffer, making that a free operation.

Why not use something like KryoNet?

https://github.com/EsotericSoftware/kryonet

Why not use something like KryoNet?

https://github.com/EsotericSoftware/kryonet

Looked at it, but it looks like you need to have their proprietary client in order to connect to their server. (And i can't do that as the client isn't written in java.)

Additionally, i plan to release the source code of the server (for potential modding purposes) and not having to worry about any kind of code licensing is a nice bonus.

What it did for the time being is to rewrite the server and reduce most of the stuff to only specific threads. (And add a message Queue for passing messages from the TCP reciever thread to the rest of the system.)

The TCP communication still works with the "one connection per thread" approach, but i can now easily swap that out in the future due to better code abstraktion in the server source code. (Once i get NIO working properly.)

I still have to profile that stuff though...

Looked at it, but it looks like you need to have their proprietary client in order to connect to their server.
Proprietary?

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Looked at it, but it looks like you need to have their proprietary client in order to connect to their server. (And i can't do that as the client isn't written in java.)

Additionally, i plan to release the source code of the server (for potential modding purposes) and not having to worry about any kind of code licensing is a nice bonus.

Any networking library is going to have 'server' and 'client' entities that wrap up all the socket stuff and hide the details. These are just regular old objects that you use to create your actual server and client software. They aren't requiring you to use any sort of proprietary software, which seems to be what you are alluding to.

And Kryonet is open source with a liberal license (the BSD three-clause). It won't impact releasing the source of your project at all, so there's no "code licensing" to worry about. If you're doing this as a learning exercise, that's fine, but if you want to just get stuff done, Kryonet is a fine option.

Looked at it, but it looks like you need to have their proprietary client in order to connect to their server.
Proprietary?

My bad. (Bad wording.) What i ment was that you need their client java code/api in order to connect to the server (which i sadly can't use as the game isn't written in java.)

Looked at it, but it looks like you need to have their proprietary client in order to connect to their server. (And i can't do that as the client isn't written in java.)

Additionally, i plan to release the source code of the server (for potential modding purposes) and not having to worry about any kind of code licensing is a nice bonus.

Any networking library is going to have 'server' and 'client' entities that wrap up all the socket stuff and hide the details. These are just regular old objects that you use to create your actual server and client software. They aren't requiring you to use any sort of proprietary software, which seems to be what you are alluding to.

And Kryonet is open source with a liberal license (the BSD three-clause). It won't impact releasing the source of your project at all, so there's no "code licensing" to worry about. If you're doing this as a learning exercise, that's fine, but if you want to just get stuff done, Kryonet is a fine option.

The problem is that while the server is written in java, the game isn't. This means that i would have to port the client side code in order to use it. (Which isn't worth it due to too much language differences.)

But thank you all for responding and giving me advice on this topic.

I've never done stuff with java threads, but I may add that you need to would look into how do the threads interact with the current GC to prevent memory leaks (you would probably need to kill the thread/unreference the thread/sleep the thread to get the garbage collected). Probably a thread queue would do good enough.

The problem is that while the server is written in java, the game isn't. This means that i would have to port the client side code in order to use it. (Which isn't worth it due to too much language differences.)


You're already doing that anyway, so how does using Kyronet mean you can't continue? You can use Kyronet or any other networking library to implement the server and then use any language + library combination you want to implement the client. All the client has to do is open a single socket, connect to the server, and start parsing packets. It need neither know nor care how the server is implemented.

This topic is closed to new replies.

Advertisement