Skip to content

Version 2.0

Thorben Kuck edited this page Sep 20, 2018 · 6 revisions

Why was Version 2.0 necessary?

NetCom2 was originally founded on so called "blocking" communication. Blocking communication can understood like this:

You have a Socket. Reading from a socket is blocking, aka by reading from the Socket, the current Thread blocks until the reading operation is complete. This means, a Thread is needed for each Socket that is connected.

So, why exactly is this bad? On the surface, this does not sound that bad. But if you think about the costs of a Thread (which is roughly 1 MB per thread), you can see the problem. If you where to use a blocking Communication in a scenario, where you have 1000 Clients connected, you would already have 1 GB of Ram usage.

To compensate this, we want non-blocking communication (which can be found within the java.nio package). You can find much better articles online, than try to listen to a stranger in a github wiki.

But the rework was needed, to allow nio to be used within NetCom2. The design of NIO is somewhat strange (do not reed the commit messages, if you are a fan of NIO). The original design did allow NIO to work, but it was extremely inefficient and very cost intensiv. It was like a tumor, growing on the side of NetCom2. Scalability looks different. It did not bring any improvement.

With the redesign,this changed. A Server can natively handle up to 1024 Clients with one Thread. If you want to test the limits, be prepared to use multiple computers. On will run out of RAM, because the simple amount of clients is to much.

Internally we needed a form of an eventloop. This eventloop takes events (mostly read-events) and collects them from the Connections. With NIO, a non-blocking eventloop uses a selector to find Connections, that have a read-event. With blocking communication those eventloops provide those connection with a callback, that will be called, once the reading thread for the blocking communication received a chunk of information.

Since the Connections had to changed quit a bit, to allow for such a design, this rework is quit major. There no longer is a SocketFactory, but a ConnectorCore. The eventloop is more or less passiv and gets the connections, that it should handle through said connector core. Further, the Thread-management had to be redesigned. Whilst we where on it, seeing that a major, breaking rework was needed, we introduced new designs and features, that make working easier and faster.

What has changed?

With Version 2.0, the list is long. We extended upon modularity, made naming conventions more clear, enhanced features so on and so forth. If you have (or want) to migrate from Version 1.0.2, take a look at the Version 2 Migration Guide.

Within the core, we changed the base handshake between the ClientStart and the ServerStart. This was a harsh thing for us to do, but it allowed for a big problem to be fixed. The Connection establishment within a ClientConnectedHandler. This problem lead to infinite recursion and really needed a fix.

The down-side to this is, that we broke compatibility with Version 1.*. This may be fixed within the next release for Version 1.*, if we keep working on that, but for now, this is the case.

What about the threading system?

The Thread-Management has been redesigned to no longer have a Thread created whenever, but to have "worker-tasks", that try to find Runnable "Tasks" and work on them. The main class to look at, is the com.github.thorbenkuck.netcom2.utility.threaded.NetComThreadPool.

This class basically encapsulates a ExecutorService. This thread pool holds worker tasks. By calling NetComThreadPool.submitTask(/* the runnable */);, our task will be passed to any available worker-tasks. If you just want to have a parallel Runnable run (in its own Thread), you can state NetComThreadPool.submitCustomProcess(/* your "Thread" */);.

If there are no free worker-tasks, a new one will be created. There is a maxWorkerProcesses field, which defines how many worker tasks there can be. It also has a allowOverlow field, which defines whether or not, there may be more than the maxWorkerProcesses running at any time. Once a Worker task is done, it checks whether it is needed or not.

So let's say, there is a very workload intensive time-period. We have to handle 20 Runnables at the exact same time. By default, the NetComThreadPool will now compensate this issue, that there aren't enough Worker-Tasks to handle those tasks fast by starting multiple separate worker tasks, to work faster on the current harsh flow of tasks. Let's say it starts 20 Worker-Tasks and there where 4 Worker-Tasks already running, so there are no 24 Worker-Tasks. After this "storm of tasks", we do not need 24 Worker-Tasks anymore. The Worker-Tasks will check, once they are finished, whether or not they are needed. Since those 24 Worker-Tasks are way to many (to be exact, they are 14 above the default maxWorkerProcesses setting), the first one will terminate itself, then the second one up until there are only 10 Worker-Tasks running.

You can get a diagnostic output of the NetComThreadPool state like this:

NetComThreadPool.generateDiagnosticOutput();

This is just a String, so you can read this. It will look something like this:

WorkerProcess States (available/total): 5/7

You have no direct influence over when and if a Worker-Task is removed to free up resources. A Worker-Task will check by itself if it can be removed and, if possible remove it self. This is done, once a Worker-Task is finished and a Worker-Task is finished, whenever it finishes a Task.

NOTE: This ThreadPool is not starvation free by design! Your custom processes will always have the same priority as all the Worker-Tasks, but there might be a Worker-Task that will take a long time to get a Task.

How can i create a custom Connection-System with the new design?

With the old Design, you could simply provide a SocketFactory, that instantiates the Socket. If you wanted to introduce SSL, you easily could create a SocketFactory, that created SSLSocket instances. Now, this is not so easy any more. You will have to provide a custom ConnectorCore, that (upon calling establishConnection) creates an SSLSocket (or channel, or whatever). This would mean, you would have to introduce the EventLoop system and so on. There are no abstract classes for you to use at the moment. They will come with the next release.

Of course, you can also establish Connections to Files, USBDevices or what ever. This would be way more complicated, because you would also have to create a custom Connection (since by default they are just meant for Sockets, SocketChannels and DatagramSockets), but of course, it would be possible.

Clone this wiki locally