netty/netty — pull request #2470 by jakobbuchgraber (2014-05-07 13:51:13+00:00)
Title: [GSoC] Netty: Pluggable algorithm to choose next EventLoop

Hello,

here is a first draft of what I think how #1230 should be implemented.

```EpollEventLoopGroup``` and ```NioEventLoopGroup``` get additional constructors to accept an ```EventLoopChooser``` object. At its heart the ```EventLoopChooser``` has a method ```EventLoop next(SocketAddress remoteAddress)```, which is called on each accepted channel and maps each channel to an ```EventLoop```. The ```remoteAddress``` parameter is the IP address of the accepted channel. That's useful in cases where a developer wants to map channels to event loops based on IP address or subnet. However, it might also be null, in cases where e.g. a task is assigned an event loop. So developers have to ensure that their code works in both cases. Most developers will simply want to ignore the parameter and assign channels to event loops based on some other criteria, e.g. provided by a ```MetricsProvider```.

So what's a ```MetricsProvider```? A ```MetricsProvider``` is mostly (85,7% to be precise) a ```MetricsCollector``` and the latter one has lots of methods that are called in the different ```Channel``` implementations to collect data about what's their inside. Like the number of registered channels, transferred bytes, etc. Each event loop gets its own ```MetricsCollector```, that way a developer can implement a ```MetricsCollector``` to provide metrics on a per event loop basis and use this information in the ```EventLoopChooser#next(SocketAddress remoteAddress)``` method to decide which channel is mapped to which event loop.

In order to make metrics computation easier, some basic data structures for (what I believe to be) common use cases are provided. One such data structure is what I call the ```TickValueHistory```.

A ```TickValueHistory``` object simply provides the sum of the last n ticks (think n seconds) through a call to ```TickValueHistory#value()```. It can be used to answer queries like "how many bytes were transferred in the last n seconds?". It's usage is illustrated in the ```DefaultMetricsProvider``` class. Basically, a timer increments an integer ```currentTick``` every second and whenever bytes are read from a channel ```TickValueHistory#update(currentTick, bytesRead)``` is called. The main advantage of this design is that it avoids lots of calls to ```System.nanoTime()``` and reduces the "hot path" to two simple integer additions. Furthermore, I am aware that on the accuracy of Netty's scheduled task times can't be counted on (1 second, might really be 2 seconds if the event loop is real busy). I am not sure how much of a problem this will be in practice and so I plan to do some experiments to see it for myself. If times turn out to be really inaccurate, an easy fix would be to simply call System.nanoTime() every tick (think "netty second") and normalize the sum to a real second (that is, divide the sum by the actual time passed).
Also I did not use Fenwick Trees (as opposed to what I wrote in my proposal). The reason being that I think that the generality they provide is simply not needed. Basically, with a Fenwick Tree one could get the sum for arbitrary periods of time (at a greater cost), while the current implementation only allows to look back a specific period of time. In other words, with Fenwick trees the ```numPastTicks``` parameter would not be in the constructor but in the ```value``` method.

What's still missing besides tests and comments?
* Add another data structure to provide a moving average. That's useful to answer questions like "What was the average pipeline traversal time in the last m seconds?"
* Add more metric collection methods. I would like to have some more discussions about what's really useful tough, in order to not add metric hooks prematurely.
* Add metric hooks to all transports.
* Benchmarks and lots of testing
* Some ```EventLoopChooser``` implementations to demonstrate the usefulness of all of this.
* Address @normanmaurer's and @trustin's concerns :)