Monday, 28 January 2013

Tomcat Clustering Series Part 5 : NginX as Load Balancer

Hi this is my fifth part of the Tomcat Clustering Series. In this post we are going to discuss how to replace Apache httpd load balancer to Nginx as a load balancer. [Check the video below for better understanding]

Hi i finished Tomcat Clustering Series. In these series we have 2 big setups needed for Tomcat Clustering
  1. Setup and configure the Load Balancer
  2. Configure the Session Replication in Tomcat.




In first setup, we configure the Load Balancer using Apache httpd web server. Its open source and widely used.
Second setup is session replication in Tomcat through modify/add <cluster> tag in server.xml file. These tomcat instance may run on single machine or different machine.



In our Tomcat Clustering Series, the big part is configure the Load balancer (Apache httpd web server). Because there are lots of steps are involved
  1. Install the Apache httpd web server from either source or repo
  2. Compile and install the mod_jk connector
  3. Create the workers.properties file (add tomcat IP and ajp port information )
  4. Configure into httpd.conf file (Add mod_jk related settings)
these setups are take more time and very difficult to debug.

Is there is any alternative to load balancer?
    Yes, Nginx its emerging web server. this post we are going to discuss how to setup nginx as a load balancer in our tomcat cluster.

What is Nginx?
      Nginx is open source and alternative to Apache httpd web server. Its provide more performance and little memory foot print.

Install Nginx
    we can install nginx through either repository (apt-get,yum) or from source. here i m build nginx from source from here. then extract the compressed file.
./configure --help      
above command shows possible command line options available for compile
for install use this command
./configure   --prefix=/home/ramki/nginx   --with-http_ssl_module     
here
--prefix used to specify where nginx server want to install, here i m using my home folder (like /usr/local/nginx)
--with-http_ssl_module here i specified install SSL module (https), its not necessary. If we want secure webpage then this module is needed.

then compile the source
 make      

install the nginx based on our configuration
sudo make install     

now installation is done, To start the nginx
cd /home/ramki/nginx/sbin   
sudo ./nginx     

now open browser and go to http://localhost to get nginx default page.

To stop the nginx, we need to pass stop signal via -s option
sudo ./nginx  -s stop     

Configure as a Load Balancer
     Nginx configurations are stored in nginx.con file in conf/ folder.  We need to perform 2 steps to make nginx as load balancer.

First define Upstream block in nginx.conf file

            Upstream block provides simple load-balancing across upstream (backend) servers. In upstream block have unique name (here  tomcatcluster), later we need to point out from other block.

       upstream tomcatcluster  {
                server 127.0.0.1:8181;
server 127.0.0.1:8282;
server 127.0.0.1:8383;
         }

here we define the back-end servers (Tomcat instances) via server directive.
In each server directive we mention IP address of server and HTTP port number(Not AJP port number). 

Second forward (proxy) the request to back-end servers

             This step is forward the request to upstream blocks, where we define the back end servers.

 location / {
             proxy_pass http://tomcatcluster;
        }   

here if location / is already present, then remove and add this new one.

here proxy_pass directive, proxifier(forward) the requests to upstream servers. here tomcatcluster is name of the upstream block and we used http protocol to forward the request.

that's it. Load balancer is ready.

now start the tomcat instances based on my previous post (session replication). then start nginx.
now access http://localhost.

now nginx forward the request in round robin fashion to back-end servers. all servers have the session, so it can able to process the request

This is same as non-sticky session method we used in my previous posts.


If we want sticky session, then nginx directly not provide, but some 3rd party patches are available. but nginx provide ip_hash directive. Its forward all request from single IP to same tomcat like sticky session. But sticky session works based on cookie (session). but ip_hash is based on IP address of client.

If u want to configure ip_hash, then modify the upstream block, add ip_hash

upstream tomcatcluster  {
                ip_hash;
                server 127.0.0.1:8181;
server 127.0.0.1:8282;
server 127.0.0.1:8383;
         }

just restart the server, u feel like sticky session concept.

when we compare the Nginx to Apache httpd web server, nginx is provide simple configuration, and can handle huge amount of traffic, consume little memory foot print and its take little CPU load.
search Nginx vs Apache we get some amazing benchmark results. 

Screen Cast :



Wednesday, 5 December 2012

Tomcat Clustering Series Part 4 : Session Replication using Backup Manager

Hi this is my fourth part of the Tomcat Clustering Series. In this post we are going to discuss the how to setup session replication using Backup Manager in tomcat clustering environment. Session replication makes High availability and full fail-over capability to our clustering environment.[Check the video below for better understanding]

Its continuation of the last post (session replication using Delta Manager). In delta manager each tomcat instance need to replicate the session information to all other tomcat instances. Its take more time and replication if our cluster size is increased. so there is alternative manager is there. Its Backup Manager.

Backup Manager is replicate the copy of session data to exactly one other tomcat instances. This big difference between both managers. here which tomcat creates that is primary copy of the session. and another tomcat whose hold the replicate session is backup copy. If any one of the tomcat is down. back up tomcat serve the session. Its achieve the fail over capability.

The setup process of backup manager is same as Delta manager. except we need to mention the Manager as BacupManager (org.apache.catalina.ha.session.DeltaManager)  inside <Cluster> element.


Suppose we have 3 tomcat instances like previous post. and i configured into backup manager.

now user try access the page. User request comes to load balancer, and load balancer redirect the rquest to suppose tomcat1. Now tomcat one create the session, now tomcat1 is responsible to replicate exactly one copy to any one of the tomcat. so tomcat1 picks any tomcat which is part of the cluster (multicast). here tomcat1 picks tomcat3 as a backup. so tomcat3 hold the backup copy of the session.

we are run the load balancer in sticky session mode. so all further request from that particular user  is redirect to tomcat1 only. all modification in tomcat1 is replicate to tomcat3.

now tomcat1 is crashed/shutdown for some reason



now same user try to access the page. this time load balancer try to redirect to tomcat1. but tomcat1 is down. so load-balancer pick one tomcat from the remaining tomcats. here interestingly 2 case are there.

Case 1:

  Suppose Load balancer pick the tomcat3 then tomcat3 receive the request and tomcat3 itself hold the backup copy of the session. so tomcat3 make that session as primary copy and tomcat3 pick any one tomcat as backup copy. so here remaining only one tomcat is there. so tomcat3 replicate the session to tomcat2. so now tomcat3 hold primary copy and tomcat2 hold the backup copy. now tomcat3 give the response to user. all further request is handled by tomcat3 (sticky session).

case 2:

 Suppose Load balancer pick the tomcat2 then tomcat2 receive the request and tomcat2 don't have the session. so tomcat2 session manager (Backup Manager) ask to all other tomcat manager "hi anybody hold the session for this user (based on session id [cookie])". Actually tomcat3 have the backup session. so tomcat3 inform to tomcat2. and replicate the session to tomcat2. now  tomcat2 make that session as primary copy and tomcat3 whose already have copy of session as remains as a backup copy of that session. so now tomcat2 hold primary copy and tomcat3 hold the backup copy. now tomcat2 give the response to user. all further request is handled by tomcat2 (sticky session).

so in either case our session is replicate and maintained by backup manager. Its good for large cluster.
check the video below

check my configuration in my github repo or get as ZIP file


Note:


Load balancer also faces single point failure. to resolve this we need to put another load balancer with public address and update the new IP to DNS server with same URL. so our URL like example.com query resolves the 2 IP address for 2 load balancer.

How its work:-
-> If browser want to access http://example.com then it first ask DNS server.
-> DNS server gives 2 IP address to browser
-> Browser take the first IP address and try to connect.
-> If in the case that server is failed to respond then browser side make timeout
-> then browser contact second IP address, now second load balancer is works fine.

This kind of adding more load balancer makes to our website more scalable and reliable in case of tragedy.

check in ur terminal

>     nslookup google.com      

its gives 11 IP address. these all are google load balancer's located in various geographic locations


Another thing this DNS servers not return same order of IP list to browser. each an every time its rotate(round robin) the IP list. so in 2 different machine ask google.com get different order of IP list. so these 2 differnt machine connect different google load balancer. so here DNS server also play little role for distribute the requests(loads).

to verify use same command twice. and verify the IP order.



Screen Cast:


Tuesday, 20 November 2012

Tomcat Clustering Series Part 3 : Session Replication

Hi this is my third part of the Tomcat Clustering Series. In this post we are going to discuss the how to setup session replication in tomcat clustering environment. Session replication makes High availability and full fail-over capability to our clustering environment.[Check the video below for better understanding]

In my previous post we discussed about setup simple load balancer and how to make session affinity concepts.

How to setup Session Replication in tomcat
   before going to session replication we need to understand 2 important concepts
  • Multicast
  • Session Manager in Tomcat
Multicast

     Multicast is To transmit a single message to a select group of recipients. here multicast used by tomcat cluster to identify the instances those part of cluster. 

There is 2 types of cluster
  • Static Tomcat Cluster
  • Dynamic Tomcat Cluster
In static cluster there is no need multicast, because each tomcat we statically defined/configured the other instances. But dynamic Cluster we are not defined anything. so each tomcat in that cluster some how to identify the other tomcat instances.

so here multicast concepts is used. each and every tomcat first joining to single multicast group. and send the heartbeat signals in periodic interval. so other tomcat instances received these signal and add the member to the cluster.

Session Manager in Tomcat

      Session Manager is used to create and manage the session behalf the application. In Servlet Specification request.getSession(); line is mention that container (tomcat) is responsible for create the session. here tomcat use the Session Manager for this purpose.

there is 4 types of Session Manager
  • Standard Manager
  • Persistent Manager
  • Delta Manager
  • Backup Manager

Standard Manager

Its is the default manager used by tomcat. Even though we are not mention in our web application tomcat use this manager for managing our session. If u want to customize the this standard manager then add <Manager> tag in context.xml file.

<Manager className=“org.apache.catalina.session.StandardManager” />

here org.apache.catalina.session.StandardManager is fully qualified class name of the Standard Manager.

Persistent Manger

This mnager is to sote the session information into persistent place after some interval. here two types of store is available.
  • File Store
  • JDBC Store
File Store helps to store all session information in separate files in underlying file system (local HDD or shared file-system like NFS,..) 
JDBC Store helps to store the session information to relational database.

so using the Persistent Manager we can achieve the tomcat cluster. but its not swapped out in real time. its pushes the information after certain interval. so if anything badly happen(crash) before that interval then in-memory session data is gone.

Delta Manger

In this post we are going to use this manager. Its replicate the session to all other instances. so this manager usually used clustered environment. but not good for large cluster.

Backup Manager

this manager usually used clustered environment. Its like delta manger. but it will  replicate to exactly one other instance(backup instance). Its acted like one instance is Primary  and another instance as backup  

Steps to make Session Replication in Tomcat Clustering

here i will continue from exactly where i left in last session affinity post. so check that post and make sure jumRoute all are set properly. so steps are
  1. Enable Multicast routing
  2. Add <Cluster> Entries in conf/server.xml file for all instances.
  3. Enable the Web Application as distributable

1. Enable Multicast routing


       In Linux Environment most of the system kernel is capable to process the multicast address. but we need to add route entry in kernel routing table.
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

here eth0 is my Ethernet interface. so change according to your  interface
In multicast address is belong to Class D address Range (224.0.0.0 to 239.255.255.255). so we inform to kernel if any one access these address then it goes through eth0 interface.

2. Add <Cluster> Entries in conf/server.xml file for all instances.


     This very important part for tomcat clustering. We need to Add <Cluster> tag in conf/server.xml file in all tomcat instances.

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>

we can add this <Cluster> tag in either inside the<Engine> tag or <Host> tag.

here SimpleTcpCluster is Tomcat Cluster implementation

This tag is looks like simple but its has many inner tags. if we omitted then its takes the default values. if we want do any cutomization (like change multicat address, receving address port) we need to use complete <Cluster> tag

this is complete <Cluster> 


<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" 
                        channelSendOptions="8">

<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>

<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Interceptor  
                                  className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor 
                  className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>

<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>

<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>

</Cluster>


Check my sample conf/server.xml file (for reference)

here most of the code are  boiler plate code. just copy and paste. if we need we can customize. for example we can change the multicat address and port number.

<Manager className="org.apache.catalina.ha.session.DeltaManager"/>

here Manager tag define the delta manager. Delta manager means replicate to all instances.


<Channel className="org.apache.catalina.tribes.group.GroupChannel">
Tomcat Clustering use the Apache Tribes communication framework.  This group commnication framework is responsible for dynamic membership (using multicast) , send and receive the session delta information using uni-cast (normal TCP connection).


<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
This is Membership definition. here address is multicast address. we can pick any address from Class D address range (224.0.0.0 to 239.255.255.255)and any port number.

Each and every tomcat send the heart beat signal to multicast address in periodic (frequency) interval. all other tomcat whose joined the multicast address they can receive these signals and add the membership to the cluster. if heat beat signal is not revive some particular interval (dropTime) from any one of the tomcat, then we need to consider that tomcat is failed.

Note:-
     All tomcat instances which is part of the clustering, should have same multicast address and port number.


<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
here sender use the PooledParallelSender have pooled connections to use the send the session information concurrently. so its speedup the session replication process.


<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
here we define which port Receiver can bind and used for receiving the session replicate information. here two properties are important. address and port. here address is ur system IP address and port is any unused port. here address="auto" its automatically pick the system IP address.


we have some interceptor
TcpFailureDetector -Its ensure that instance are dead. In some case multicast messages are delayed, all tomcat instances are think about that tomcat is dead. but this interceptor to make tcp unicast to failed tomcat and ensure that instances is actually failed or not

another important listener is JvmRouteSessionIDBinderListener, we talk about later

3. Enable the Web Application as distributable


   We need to make the our web application distribuable. its simple add <distributable/> tag in web.xml file. In according to servlet specification  <distributable/> tag in web.xml mention that any container to consider this application can work in distributed environment.

Note:
All session in our web application must me serializable.


Do these steps to all tomcat instances and start the tomcat and httpd server. check my configuration in my github repo or get as ZIP


This is my configuration. all 3 tomcat instances are configured in delta manager and i deployed the distributed web application. all tomcat use the multicast to maintain the membership.

now client make the request and first tomcat process and create the session, then looks like this
then tomcat 1 is responsible to replicate the session using Apache tribes group communication framework to replicate the session to all instances.

now all tomcat instance have exact copy of the session. so if tomcat 1 crashed or shutdown, then any other tomcat still can process the request [see the video below]

We used session affinity like previous post. based on that cookie id contain the tomcat name (worker name). so when first tomcat1 return the session id end with tomcat1. but when tomcat 1 is failed and tomcat 2 take the responsible for all further request. but session id still contain the tomcat1. so its makes the load balancer difficult. because tomcat1 is down. and load balancer pick any other tomcat. but actually tomcat2 takes the responsible. so we need to reflect these changes in session id.
JvmRouteSessionIDBinderListener  take care to change the client session id to tomcat2 when failure is occurred  so load balancer redirect to tomcat2 without confusing.

Check the git hub for all configuration files and tomcat clustering setup is available. or u can download as ZIP

Reference:




Screen Cast:


Tuesday, 30 October 2012

Tomcat Clustering Series Part 2 : Session Affinity Load Balancer

Hi this second part of the Tomcat Clustering Series. In my first part we discuss about how to setup simple load balancer. and we seen how load balancer distribute the request to tomcat instance in round robin fashion. [Check the video below for better understanding]

In this post we discuss about what is the problem is occur in simple load balancer when we introduce sessions in our web application. and we will see how to resolve this issue.

Its continuation of my first part of this series. so kindly go read my first part then continue here.

How Session works in Servlet/Tomcat?
       Before going into problem, let see the session management in Tomcat.
If any if the page/servlet create the session then Tomcat create the Session Object and attached into group of session (HashMap kind structure) and that session can identify using session-id, its just random number generated through any one of the hash algorithm. then respond to client with cookie header field. That cookie header field are key value pair. so tomcat create jsessioid is the key and random session-id is the value.

Once response reached to client (Web Browser) its update the cookie value. If already exist, then its overrides the cookie value. Then all further communication browser send the cookie attached with request to that server.

HTTP is stateless protocol. so server can't find the client session usual way. so server reads the header of the request and extract the cookie value and server got the Random session-id. then it search through group of session maintained by the tomcat. (It usually hash-map). then tomcat got perfect Session of that particular client (Web Browser).

If client cookie value doesn't match with group of sessions then tomcat create the completely new session and send the new cookie to browser. then browser update it.


this index.jsp code to deploy all tomcat instances
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.Date"%>

<%@page import="java.util.List"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>


    
        
        JSP Page
    
    

        Instance 1 


        
       
        
Session Id : <%=request.getSession().getId()%> Is it New Session : <%=request.getSession().isNew()%> Session Creation Date : <%=new Date(request.getSession().getCreationTime())%> Session Access Date : <%=new Date(request.getSession().getLastAccessedTime())%> Cart List
    <% String bookName = request.getParameter("bookName"); List listOfBooks = (List) request.getSession().getAttribute("Books"); if (listOfBooks == null) { listOfBooks = new ArrayList(); request.getSession().setAttribute("Books", listOfBooks); } if (bookName != null) { listOfBooks.add(bookName); request.getSession().setAttribute("Books", listOfBooks); } for (String book : listOfBooks) { out.println("
  • "+book + "
  • "); } %>

Book Name



What is the Problem In Simple Load Balancer?
     If we deploy the web application, in which its used the session then actual problem is occurred.

I describe with sequence

1. User request one web page, in that web page its used sessions (like shopping cart).
2. Load balancer intercept the request and use the round robin fashion its send to one of the tomcat. suppose this time its send to tomcat1.
3. tomcat1 create the session and respond with cookie header to client.
4. load balancer just act as relay. its send back to client.


5. next time user request again the shopping cart to server. this time user send the cookie header also
6.Load balancer intercept the request and use the round robin fashion its send to one of the tomcat. this time its send to tomcat2.
7. Tomcat 2 receive the request and extract the session-id. and this session id is doesn't match with their managed session. because this session is available only in tomcat1. so tomcat 2 is create the new session  and send new cookie to client
8. Client receive the response and update the cookie(Its overwrite the old cookie).


9. Client send one more time to request that page and send the cookie to server.
10. Load balancer intercept the request and use the round robin fashion its send to one of the tomcat. this time its send to tomcat3.
11. Tomcat 3 receive the request and extract the session-id. and this session id is doesn't match with their managed session. because this session is available only in tomcat2. so tomcat3 is create the new session  and send new cookie to client
12. Client receive the response and update the cookie.(Its overwrite the old cookie).


13. Client send one more time to request that page and send the cookie to server.
14. Load balancer intercept the request and use the round robin fashion its send to one of the tomcat. this time its send to tomcat1.
15. Tomcat 1 receive the request and extract the session-id. and this session id is doesn't match with their managed session. because client session id is updated by tomcat 3 last time. so even though tomcat 1 have one session object created by this client. but client session id is wrong.so tomcat1 is create the new session  and send new cookie to client (for more info watch the video below)
16. Client receive the response and update the cookie.


this sequence is continue ...

as the result every request one session is created. instead of continue with old one.


Here root cause is Load balancer. If load balancer redirect the request correctly then this problem is fixed. but how load balancer know in advance about this client before is processed by particular tomcat.
HTTP is stateless protocol. so HTTP doesn't help this situation. and other information is jsessionid cookie. its good but its just random value. so we can't take decision based on this random value.
ex:
Cookie: JSESSIONID=40025608F7B50E42DFA2785329079227


Session affinity/Sticky Session

Session affinity overrides the load-balancing algorithm by directing all requests in a session to a specific tomcat server. so when we setup the session affinity our problem is solved. but how to setup because session values are random value. so we need to generate the session value some how identify the which tomcat generate response.

jvmRoute

Tomcat configuration file (server.xml) caontain  <Engine> tag have jvmRoute property for this purpose. so edit the config file and update the <Engine > tag like this

<Engine name="Catalina" defaultHost="localhost“ jvmRoute=“tomcat1” >

here we mention jvmRoute="tomcat1" here tomcat1 is worker name of this tomcat. check the workers.properties file in last post

Add this line to all tomcat instances conf/server.xml file and change the jvmRoute value according to workers name and restart the tomcat instances.

Now all tomcat generate the session-id pattern like this
<Random Value like before>.<jvmRoute value>

ex: tomcat1 generate the session id  like
Cookie:JSESSIONID=40025608F7B50E42DFA2785329079227.tomcat1


here tail have which tomcat generate the session. so load balancer easily find out to where we need to delegate the request. in this case its tomcat1.

so update all tomcat instances conf/server.xml file to add the jvmRoute property to appropriate worker name values. and restart the instances. all problem is fixed and entire load balance works fine even session based application.

but there is still one drawback

if 5 user acessing the website. In session affinity is setup. here

tomcat 1 serves 2 user,
tomcat 2 serves 2 user, 
tomcat 3 serves 1 user,  then suddenly one of instance is failed, then what happen?

suppose instance 1 (tomcat1) is failed, then those 2 users lost their session. but their request are redirect to one of the remaining tomcat instances (tomcat2,tomcat3). so they still access the web page. but they lost previous sessions. this is one of the draw back. but its compare to last post load balancer. its works in session based web application also.

next post we will see how to set up the session replication in load balancer.


please share ur thoughts through comments.

Update : Check the Session Replication Process in Third Part of this Series


Screen Cast:

Wednesday, 10 October 2012

Tomcat Clustering Series Part 1 : Simple Load Balancer

I am going to start new series of posts about Tomcat clustering. In this first post we will see what is problem in normal deployment in only single machine, what is clustering and why is necessary and how to setup the simple load balancer with Apache httpd web server + Tomcat server cluster.[Check the video for better understanding]

Why need Clustering? (Tomcat Clustering) 
          In normal production servers are running in single machine. If that's machine may be failed due to crashed or hardware defects or OutOfMemory exception then  our site can't access by anybody.
so how to solve this problem?. to add more tomcat machine to collectively (group/cluster) run as a production server. (oppose of single machine). Each tomcat has deployed the same web application. so any tomcat can process the client request. If one tomcat is failed, then other tomcat in the cluster to proceeds the request.

Here one big problem is arrive. each tomcat instances are running in dedicated physical machine or many tomcat instances are running in single machine(Check my post about Running multiple tomcat instances in single machine). so each tomcat running on different port and may be in  different IP.

the problem is in client perspective, to which tomcat we need to make the request? because there are lots of tomcat part of clustering is running. each tomcat we need to make IP and Port combination. like
http://192.168.56.190:8080/ or http://192.168.56.191:8181/
so how to solve this problem?
To add one server in-front of all tomcat clusters. to accept all the request and distribute to the cluster. so this server acts as a load balancer. There is lots of server is available with load balancing capability. here we are going to use Apache httpd web server as a load balancer. with mod_jk module.
so now all clients to access the load balancer (Apache httpd web server) and don't bother about tomcat instances. so now ur URL is http://ramkitech.com/ (Apache runs on port 80).

Apache httpd Web Server
           Here we are going to use Apache httpd web server as a Load Balancer. To provide the load balancing capability to Apache httpd server we need to include the either mod_proxy module or mod_jk module. here we are using mod_jk module.

Before continuing this post check my old post (Virtual Host Apache httpd server) about How to install the Apache httpd server and mod_jk module. and how to configure the mod_jk.

How to setup the Simple Load Balancer
         For simplicity purpose i going to run 3 tomcat instances in single machine(we can run on dedicated machine also) with Apache httpd web server. and single web application is deployed in all tomcat instances.
here we use mod_jk module as the load balancer. by default its use the round robin algorithm to distribute the requests. now we need to configure the workers.properties file like virtual host concept in Apache httpd server.

worker.list=tomcat1,tomcat2,tomcat3

worker.tomcat1.type=ajp13
worker.tomcat1.port=8009
worker.tomcat1.host=localhost

worker.tomcat2.type=ajp13
worker.tomcat2.port=8010
worker.tomcat2.host=localhost

worker.tomcat3.type=ajp13
worker.tomcat3.port=8011
worker.tomcat3.host=localhost

here i configure the 3 tomcat instances in workers.properties file. here type is ajp13 and port is ajp port (not http connector port) and host is IP address of tomcat instance machine.

there are couple of special workers we need add into workers.properties file.

First one is add load balancer worker, here the name is  balancer (u can put any name).


worker.balancer.type=lb
worker.balancer.balance_workers=tomcat1,tomcat2,tomcat3

here this worker type is lb, ie load balancer. its special type provide by load balancer. and another property is balance_workers to specify all tomcat instances like tomcat1,tomcat2,tomcat3 (comma separated)

Second one, add the status worker, Its optional. but from this worker we can get statistical of load balancer.

worker.stat.type=status

here we use special type status.

now we modify the worker.list property.

worker.list=balancer,stat

so from outside there are 2 workers are visible (balancer and stat). so all request comes to balancer. then balancer worker manage all tomcat instances.

complete workers.properties file

worker.list=balancer,stat

worker.tomcat1.type=ajp13
worker.tomcat1.port=8009
worker.tomcat1.host=localhost

worker.tomcat2.type=ajp13
worker.tomcat2.port=8010
worker.tomcat2.host=localhost

worker.tomcat3.type=ajp13
worker.tomcat3.port=8011
worker.tomcat3.host=localhost


worker.balancer.type=lb
worker.balancer.balance_workers=tomcat1,tomcat2,tomcat3

worker.stat.type=status


Now workers.properties confiuration is finished. now we need to send the all request to balancer worker.
so modify the httpd.conf file of Apache httpd server

LoadModule    jk_module  modules/mod_jk.so

JkWorkersFile conf/workers.properties

JkLogFile     logs/mod_jk.log
JkLogLevel    emerg
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions     +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat     "%w %V %T"

JkMount  /status  stat
JkMount  /  balancer

the above code is just boiler plate code. 1st line load the mod_jk module, 2nd line to specified the worker file (workers.properties file). all others are just logging purpose.

The last 2 lines are important.
JkMount  /status  stat   means any request to match the /status then that request forward to stat worker. Its status type worker. so its shows status of load balancer.

JkMount  /  balancer this line matches all the request, so all request is forward to balancer worker. In balancer worker ist uses the round robin algorithm to distribuete the request to other tomcat instances.

That's it.
now access the load balancer from the browser. each and every request is distribute to 3 tomcat instances. If one of the tomcat instances are failed then load balancer dynamically understand and stop to forward the request to that failed tomcat instances. Other tomcat instances are continue to work. If that failed tomcat is recovered from failed state to normal state then load balancer add to cluster and forward the request to that tomcat. (check the video)

Here big question is How Load balancer knows when one tomcat instance is failed or tomcat is just recovered from failed state?
Ans : when one tomcat instance is failed, load balancer don't know about that instances is failed. so its try to forward the request to all tomcat instances. If load balancer try to forward the request to failed tomcat instance, its will not respond. so load balancer understand and marked the state as a failed and forward the same request to another tomcat instances. so client perspective we not feel one tomcat instances are failed.

when tomcat instances recovered from failed state. that time also load balancer don't know that tomcat is ready for processing. Its still marked the state is failed. In periodic interval load balancer checks the health status of all tomcat instances. (by default 60 sec). after checking health status then only load balancer came to know that tomcat instance is ready. and its update the status is OK.


please share ur thoughts through comments.

Video :

Wednesday, 12 September 2012

Integrate Chart Image into Jasper Report Part - 2

In my previous post we discussed how to Integrate the Charts into Jasper Reports using JFreeChart API in Jasper Report. In this post there is another way to embeds the charts into Jasper Reports. This method, we need to generate/create the chart using any 3rd party Java Lib and convert into Image Object or stored into File-system, then just insert the Image into Jasper Report. here no need to use chart functionality in Jasper Report. because these chart are limited functionality.


Steps:
  1. Create the chart using any Java libraries
  2. Convert the Chart into BufferedImage or Stored into files
  3. pass the Image into Jasper Report.

1. Create the chart using any Java libraries
     In this step we going to create the chart. here i m using jopenchart Java library to create the chart. In their website tutorial section contain some sample codes. so i used these codes. and download the jopenchart lib from here.

2. Convert the chart into BufferedImage or Stored into files
    In jopenchart have the in-build function for store chart into the file.
ChartEncoder.createPNG(new FileOutputStream(System.getProperty("user.home") + "/chat.png"), c);
and if u want into in-memory Image object then use render() method.
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
DefaultChart chart = ....;
Graphics2D gd=bi.createGraphics();
chart.render(gd);

In above code we create empty BufferedImage object. the get the Graphics2D object from the BufferedImage and pass the Graphics2D into chart render() method. so when render() method is called, the chart is write into BufferedImage object.

3. Pass the Image into Jasper Report
        We need to create the Jasper Report. this time we are not using chart function in Jasper reports. we using Image component of jasperreport. We have 2 options are there, pass the Image Object or Pass the Path String object where image is stored in File-system. here first i m using Image object.

we use parameter to pass into jasper report. so first create the new Parameter in Jasper Report and named "chartImage" and Parameter Class "java.lang.Object".  drag the Image Component and set the properties. Image Expression and Expression Class like
               Image Expression == >  $P{chartImage}
               Expression Class  == > java.awt.Image



If we use stored Image File then change the Parameter class to java.lang.String and Image Expression Class to java.lang.String.

here Image Expression class is decided the Jasper Report. how we add the Image. If u put "java.awt.Image" as a Expression class then we are passing the Image object. and jasper report can understand. If u mention "java.lang.String" then we pass as String that pointing to Image file. (png,jpg,gif,..).

Code:
DemoBean.java
@ManagedBean
@SessionScoped
public class DemoBean {

    int width = 640;
    int height = 480;
    BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    public DemoBean() {
        int[] quadr = {0, 1, 4, 9, 16, 25, 36};
        int[] exp = {1, 2, 4, 8, 16, 32, 64};
        double[] columns = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0};

        // Creating a data set array
        DefaultDataSet[] ds = new DefaultDataSet[3];

        // Filling all DataSets
        ds[0] = new DefaultDataSet(ChartUtilities.transformArray(new int[]{0, 6}),
                ChartUtilities.transformArray(new double[]{0.0, 6.0}),
                CoordSystem.FIRST_YAXIS,
                "Linear Growth");

        ds[1] = new DefaultDataSet(ChartUtilities.transformArray(quadr),
                ChartUtilities.transformArray(columns),
                CoordSystem.FIRST_YAXIS,
                "Quadratic Growth");

        ds[2] = new DefaultDataSet(ChartUtilities.transformArray(exp),
                ChartUtilities.transformArray(columns),
                CoordSystem.FIRST_YAXIS,
                "Exponential Growth");

        String title = "Growth Factor Comparison";



        DefaultChartDataModel data = new DefaultChartDataModel(ds);

        data.setAutoScale(true);

        DefaultChart c = new DefaultChart(data, title, DefaultChart.LINEAR_X_LINEAR_Y);

          c.addChartRenderer(new LineChartRenderer(c.getCoordSystem(),data), 1);

        c.setBounds(new Rectangle(0, 0, width, height));
        Graphics2D gd=bi.createGraphics();
        c.render(gd);

//      /*  if ur using Image file then use these code */
//        try {
//            ChartEncoder.createPNG(new FileOutputStream(System.getProperty("user.home") + "/chart.png"), c);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }


    }

    public String pdf() throws JRException, IOException {

       
        String reportPath = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/reports/Charts.jasper");

        Map parameter = new HashMap();
        parameter.put("chartImage", bi);

        JasperPrint jasperPrint = JasperFillManager.fillReport(reportPath, parameter, new JREmptyDataSource(1));
        HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
        httpServletResponse.addHeader("Content-disposition", "attachment; filename=report.pdf");
        ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
        JasperExportManager.exportReportToPdfStream(jasperPrint, servletOutputStream);
        FacesContext.getCurrentInstance().responseComplete();

        return null;
    }
}



Download the Sample Project from my  GitHub


Check the screen-cast for better understanding.

Screen cast:
Related Posts Plugin for WordPress, Blogger...