To understand why Node came into being, we should first understand what is
- a blocking I/O programming
- multi-threaded programming and
- event driven programming
So, Ms.Dimple Gusain is at the south asia's largest mall, Lulu.
Situation:
- She among many others are waiting outside the Lulu entrance to get in.
- Among these are people who have won movie tickets from an online lot conducted by the Lulu group, people who have come to shop and people who have come because they did not know anything better to do.
- Dimple too has won a ticket to this horror movie. She goes nuts about horror movies.
- Everyone needs to pay to get inside, except for the people who have won the movie tickets. Dimple is excited.
Blocking I/O programming case:
- There is just one counter and one queue.
- In the counter are 2 systems, one for giving away the entrance tickets and one for booking the movie tickets, the seat management system(SMS).
- The ticket counter starts issuing tickets to the people in the queue, one by one.
- The entrance tickets are issued fast but the movie tickets are not. Lets see why.
- Dimple shows up and produces the movie ticket.
- The counter checks if its a valid number. The SMS confirms it is.
- Next the SMS checks the available seats and allocates one.
- The SMS then updates the seats status, nullifies her ticket, registers her presence and notifies the account system.
- All the while this is happening, the queue is waiting impatiently for tickets. They are all on the verge of cursing Dimple but she being the chosen one, escapes unhurt.
- Another prize winner, Supi, shows up. The painful wait for the queue continues but he might not make it into the theatre. Sad but true.
Multi-threaded programming case:
- There is 1 counter per person. Not the ideal mall but lets just say Lulu takes customer services to the highest level.
- The ticker counters starts issuing tickets in all counters but looks like the wait at each counter could still be optimized. Why? here is why:
- All the counters are connected to the seat management system but at any given point in time, only one will have access to it.
- This means that if counter 1 is using it to book a seat, then counter 2(although in the process of issuing movie tickets) will be locked out from accessing the seat management system. This is to prevent accidental allocation of the same seat to Dimple and Supi. Supi doesn't mind sharing it with Dimple, although but vice versa may not be true.
- Only after the counter 1 books the seats and removes the locks, does the counter 2 get to access and book the seats.
- Thus this whole process could be summarized as follows:
- counter 1 starts booking.
- It requests control of the seat management system(SMS).
- The SMS verifies that it is not presently locked.
- Upon confirmation, it gives control to counter 1 which places a lock on it and proceeds to book the seats.
- counter 2 starts booking. So do counters 4 and 10.
- All of them requests control of the SMS.
- The SMS verifies and declines the request.
- counter 2 places the booking in queue and waits. so do counters 4 and 10.
- counter 1 finishes booking but now has to update all the other systems about the booking as mentioned in the above case point 6.
- Since all the other updates do not concern the SMS, counter 1 releases the lock on it.
- counter 2 being first in the queue, this time proceeds to lock the SMS and book the seats.
- counters 5 and 6 starts booking and request the control of SMS.
- SMS declines since counter 2 is using it.
- After counter 2 release the locks, counter 4 gets to use it and the cycle repeats for all the counters in the queue.
- There is a huge improvement over the previous case when considering the fact that entrance tickets can now be issued fast, without waiting for the movie ticket process to finish.
- However in the case of movie tickets, The time taken to verify if the seat management system is in use and then placing the locks, storing the next request in queue, releasing the locks, getting the next request from queue and then repeating all over again - - Although this case is an improvement over the previous case, this is all still wasting time when considering the movie ticket booking alone.
- Thus on careful study, it is revealed that when multiple counters access the same data, they all have to wait and do it in queue. If this was the case then why did we have multiple counters in the first place?
Event driven programming case:
- There is just one counter and one queue.
- But the ticket counter functions in a different manner.
- Suppose there are 6 persons in the queue. "A", "B", "Dimple", "C", "Supi" and "D".
- Dimple and Supi are going for the movie but others are going to shop.
- Counter takes the money from "A", and entrance ticketing system being fast, issues him the entrance ticket right away.
- Counter then accepts money from "B" and follows the same procedure.
- Its Dimple's turn next. Counter gets the movie coupon from her and passes it to SMS to confirm it. Meanwhile Dimple waits around patiently(although on reality, waiting and patience is not for Dimple)
- Counter then accepts money from C and issues the entrance tickets.
- Counter then accepts movie coupon from Supi.
- The SMS has now finished booking seats for Dimple and notifies the counter of the seat number with the ticket.
- Counter makes Supi wait, issues the ticket to Dimple and continues to process Supi's ticket by passing it to SMS.
- Counter then accepts money from D and issues the entrance tickets.
- Counter is notified of the seat number with ticket by the SMS for Supi.
- Counter stops its current activity and issues the tickets to Supi. Supi is excited as he gets a seat near to Dimple. All well.
- Thus in this case, the SMS notifies the counter whenever there is a ticket-ready-event, who otherwise can keep servicing the requests of the customers without blocking them.
- When notified the counter can issue the tickets and return to servicing the customers again.
- Since there are no locks or other overheads, there is significant time gained for the small wait that the customers have to do when issuing the tickets.
In the software world, the networked applications like the ones on the internet had many concurrent requests to be serviced at the same time usually for some operations on a common data. Multi-threading was a great alternative but soon had to face difficulties due to the overheads in locks and semaphores to sync the data access. The problem with these were that it was tremendously difficult to locate bugs if the developer had somehow missed out on locks or changing contexts for individual threads.
With the growing power of the CPUs and faster compilers and interpreters, the perpetuators of network programing once again resorted to the single process but an event-driven model, where the small wait is compensated for the complex locks and context changes in the multi-threaded environment.
Moreover the only true multi-threading is when there are multiple CPU cores, with each core dedicated to a single process. In all the other cases, the CPU clock is being shared by the individual processes which means at any given point in time there is just one process being run.
Ryan Dahl and Event Driven Programming
Ryan Dahl, the creator of Node had worked with Ruby as well as its framework Rails and had found it to be insufficient in handling application that had huge concurrent service requests. He was particularly upset by the I/O blocking programming style implemented in it. He then set out to create an event driven model for networked applications and was searching for a good language to implement it. He tried C but soon stopped due to the complexity in maintaining contexts for multiple processes. He then tried Lua, but gave up because Lua already had a lot of I/O blocking libraries along with non-blocking ones. This would cause confusion in the mind of developers.
2 important things for implementing an event driven model was that
- firstly, the functions needed to be passed as arguments as consider this example:target.on_some_event("event_name", fire this callback function);as you can see, the target.on_some_event is a function that is fed with 2 arguments, namely a string "event_name" and a function. In an I/O blocking programming model, there is no need for the function to be passed as an argument. You directly invoke it.
A sample code could explain this -
I/O blocking style:
var result = query('SELECT * FROM posts WHERE id = 1');
do_something_with(result); // direct invocation
Event driven style:
query_finished = function(result){
do_something_with(result);
}
query('SELECT * FROM posts WHERE id = 1', query_finished);
- secondly, the functions needed to remember the context in which they were declared. This is known as closures.
consider the following example:
(function(){
var count = 0;
target.on_some_event("event_name", fire this callback function(){
count++;
});
})();By the time the callback function would have been called, the parent function may already have finished execution. Yet the callback function has access to the variables in its parent function(this is known as remembering its context).
Event Loop
Event driven programming styles are also accompanied by an event loop. An event loop is a construct that runs an infinite loop checking for events and triggering their handlers.
Event loop is a parallel thread running alongside the main app thread, thus raising eyebrows on the statement that Node is a single threaded process. Well, the statement is not w.r.to the internal implementation but more in lines with the way that Node handles requests in a networked application.
Event driven programming styles are also accompanied by an event loop. An event loop is a construct that runs an infinite loop checking for events and triggering their handlers.
Event loop is a parallel thread running alongside the main app thread, thus raising eyebrows on the statement that Node is a single threaded process. Well, the statement is not w.r.to the internal implementation but more in lines with the way that Node handles requests in a networked application.
Thus any language that implements an event loop can be used for event driven programming. e.g. Ruby's Event Machine, Perl's AnyEvent and Python's Twisted.
What Ryan noticed
Ryan noticed that javascript had functions as first-class objects(that they can be passed as arguments) and also had closures. This made it a perfect fit to implement the event driven model for networked applications.
Javascript was an output of a race by Netscape people with Microsoft, as the latter was getting ready to release their first browser into the market first. The language was thus not perfect and had some cleaning to be done. Luckily for Ryan, Google come up with V8, a powerful javascript engine created to drive its insanely successful browser, Chrome.
What Ryan did
Ryan built a library of low level APIs that facilitated the creation of networked applications including building servers, sockets etc. He must have built these in C.
He then built a platform using Javascript(V8 powered):
It is this platform that is now called Node.Ryan noticed that javascript had functions as first-class objects(that they can be passed as arguments) and also had closures. This made it a perfect fit to implement the event driven model for networked applications.
Javascript was an output of a race by Netscape people with Microsoft, as the latter was getting ready to release their first browser into the market first. The language was thus not perfect and had some cleaning to be done. Luckily for Ryan, Google come up with V8, a powerful javascript engine created to drive its insanely successful browser, Chrome.
What Ryan did
Ryan built a library of low level APIs that facilitated the creation of networked applications including building servers, sockets etc. He must have built these in C.
He then built a platform using Javascript(V8 powered):
- to access these low level APIs
- to create networked apps in an event driven model using callbacks and closures
No comments:
Post a Comment