Donnerstag, 19. September 2013

clientside loadbalancing

The situation in many cases is that loadbalancing happens on the web-server.
A typical J2EE application has apache as loadbalancer in front of  more servers. Each request comes to apache and is forwarded to one of those servers. 
There a a lot of topologies how this clusters can be designed. Or even hardware solutions are common to balance the incoming requests.
(http://stackoverflow.com/questions/12526265/loadbalancing-web-sockets). Loadbalancing is a very complex and deeply discussed topic.

This post explains a very simple and robust way of loadbalancing in Rich Internet Applications (single page applications).

The solution is based on jsonp (http://en.wikipedia.org/wiki/JSONP) which allows requests to other servers.

The idea is to have a small footprint page which loads the rest via jsonp from a server choosen by chance from a list of servers.

So here we go in coffeescript

Define a list of servers

root.servers    = ["http://localhost:45567/","http://localhost:4567/","http://localhost:45567/" ]




In the following class a server is choosen by chance. If the server is not available another server of the list is choosen. Only If all servers are not available a message to the user is shown.

With this technique not only loadbalancing can be done also failover and setting up new versions without downtime can be implemented.



class root.ServerTools

 @delete_element_from_array: (element_to_delete,array) ->
    for element, i in array
           if (element == element_to_delete)
               array.removeAt(i, 1)
    return array
  
  @doAjax: (path, slist,  timeout, dfd) ->
        slist    || (slist = root.servers)
        dfd      || (dfd = $.Deferred())
        timeout  || (timeout = 2000)
        #Timeout is because of javascript issue with jsonp requests Stackoverflow - question 10093497
        index=Math.floor(Math.random()*slist.length)
        current_element = slist[index]
        url=current_element + path
        args = {url: url, type: "GET", dataType: "jsonp", timeout: timeout}
        $.ajax(args).then(dfd.resolve, \
           (xhr, text_status, error_thrown) ->
                console.log error_thrown
                slist=root.ServerTools.delete_element_from_array(current_element,slist)
                if(slist.length == 0)
                   dfd.reject(xhr, text_status, error_thrown)
                else
                   root.ServerTools.doAjax(path, slist, timeout, dfd)
        )
        return dfd.promise()

 call the promise :

      promise = root.ServerTools.doAjax("orders.jsonp", root.servers,2000 )
      promise.done (response) ->   (App.Order.transform_response_to_orders(response))
      promise.fail -> alert("no Server reachable - please try later again")




Here is the complete example:


checkout  https://github.com/erhard/emb_boot.git

sha 4cb020f3a5


You need ruby or jruby and the sinatra gem.
Start the sinatra server in bin
It servers the jsonp data and the static index.html
Go to your browser and choose localhost:4567
Now the footprint is loaded and in addition from the serverlist above (where only the port 4567 is available) the dynamic rest. The kick is that the servers do not need to be on the same maschine as the page.