Thursday, May 07, 2009

Today I will talk about the reactor pattern, which allow us to design and develop a scalable, efficient and realistic server. Reactor patterns can be easily realized using Java's NIO. Before I start with this pattern, let me try explaining why at all we need this pattern. Well, if you have written a server in any language you would know that a server needs to do following:

• Receive client requests
• Perform some request-specific task
• Return a response to the client
• Multiple requests should run concurrently
• Requests may contend for resources
• Tolerates slow, misbehaving or unresponsive clients

To accomplish all these, the only option we had was to follow a multi-threaded approach, where each thread corresponds to a connection from a client. The thraad then was responsible to read | decode | compute | encode | and send/write to the client socket. This ofcourse allowed us to produce a server which could more or less satisfy all the above resposbilities. But, the issues are multiple in this approach. The primary issue is performance under increasing load.

Other major issue using this approach are:
• Simulteneous access controls may become a bottleneck
• Threading per client is a serious overhead
• Belittling output as threads/CPU ratio increases
• Scalability issues

The primary reason for following this approach are i) blocking I/O and ii) Stream based I/O instead of block based I/O.

These concerns are major concerns and we can not ignore them for long. The solution of course exists and hence this article. Yes you guessed it right. The solution lies in the pattern called Reactor pattern. Let us see what it is all about. Well, the key answer to all above issues is MULTIPLEXING the request received form client. For this to happen, we must should make use of non-blocking socket. Yes, the good news is java has added a new package called java.nio which allow both non-blocking operations and the multiplexing. Well, now that we know, we have all that are required from an implementation point of view, let us focus on the actual pattern.

Well the patterns suggests following:
i) We should divide our service in terms of event.
ii) For each event we should introduce a separate Event Handler.
iii) Each event handler should be registered with an Initiation Dispatcher, which uses a Synchronous Event Demultiplexer to wait for events to occur.
iv) When events occurs the Event Demultiplexer notifies the Initiation Dispatcher, which in turn initiates the Event Handler associated with the event.
v) The Event Handler then process the event and calls the method that implements the requested service.

If that was too much to digest, let us relax a bit.

Now let us see what it all meant. Let us continue with our earlier example of server. First, it says we should think of dividing our service. So, in our case the events we deal with, are, Accept connection, Read, Write etc. Next, it says we should dedicate separate handler for each. Well, handler in our case could be separate classes dedicated to events like Accepting connection, Reading Data etc. Next it says, we should register each of these handlers/classes to a IntiationDispatcher. Ok, so let us do that. But how? Well, answer for us is Java's NIO. It has a selector API. So, we need to register all our socket/channel with selector. While reistering we inform selector about the event we are interested in, so that it can notify us when such event takes place. So, we inform selector that we have a server socket binded on a port and we would like to know each time there is a connection ready to be accepted. So, selector takes our instruction and waits for the event to occur. This completes our task stated by Sl. No. iii).
In (iv) it says "Event Demultiplexer notifies the Initiation Dispatcher, which in turn initiates the Event Handler associated with the event". Let us see how it happens in our case. In our case, we perform a, which notifies us about the event which occured. We loop though each of the event.

The event we registered earlier was "ACCEPT". So, if we find a "ACCEPT" event during the loop, we initiate the handler/class associated with this event. Let us assume we had a class named ConnectionAccepter class. We intiate this class, which internally accepts the connection and its associated socket. It also, registers the accepted connection with selector and inform selector that a READ event to be notified. Now, within our main loop (of events) we will also look for a new event (apart from "ACCEPT"), which is "READ". Each time a "READ" event is found, we initiate a new class associated with READ.

Now we have seen how the (iv) is applied in our case. Let us now see what (v) says. According to this, our event handler or classes dedicated to each event should initiate method/process, which does the required service. Well, we have seen what our ConnectionAcceptor class did. It did what was required. But, the class dedicated to Read is a critical one, because that is the one, which will interpreat the message sent by our remote client and process further. So our Reader class, should parse the message read and submit it to a method or service, which could process it further. Now, the processing of data will require some time and we can not block our server for this. So, we will have to disconnect our server processing from data processing. So we may create some restricted number of worked threads, which reads a queue, where messages are submitted by the Reader handler/class. These worker thread processes them one by one in parallel. This completes all our server responsibilities and yet without any major bottlenecks.
Now, our thread count need not increase as our number of client increases, because it is no more related to number of connections we are serving. So, cheers!!!

Well, before we close let us see if this pattern has any downside. Well, as every thing has good and bad side our pattern too holds this true. It does have some limited/negligible disadvantages, which are:

- The dispatcher could become relatively slow.
- Debugging could become a bit tricky.

Well, that's all for today. Please let me know your views on this.

Labels: , ,

This page is powered by Blogger. Isn't yours?