Suggested Pages

Sunday, September 2, 2012

Stateful Session Bean Clustered - Tutorial

In this post I'll show an example of a Stateful Sesson Bean Clustered that implements a simple counter.
In a few steps we'll see how to configure a load balancer with Apache HTTP Server using Session Affinity on two JBoss nodes. Then we'll see the behaviour of the implemented Session Bean after a simulated failover: when one of the JBoss nodes fails, the Session will be replicated on the other active JBoss node.

Step-1: Configure Apache HTTP Server


First we have to configure the files <APACHE-HOME>/conf/workers.properties and <APACHE-HOME>/conf/httpd-vhosts.conf.
conf/workers.properties

=\
# Define list of workers that will be used
# for mapping requests
worker.list=loadbalancer,status

# Define Node1
# modify the host as your host IP or DNS name.
worker.node1.port=8009
worker.node1.host=node1.counter.cluster.com
worker.node1.type=ajp13
worker.node1.lbfactor=1
worker.node1.cachesize=10

# Define Node2
# modify the host as your host IP or DNS name.
worker.node2.port=8009
worker.node2.host= node2.counter.cluster.com
worker.node2.type=ajp13
worker.node2.lbfactor=1
worker.node2.cachesize=10

# Load-balancing behaviour
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=node1,node2
worker.loadbalancer.sticky_session=1
#worker.list=loadbalancer

# Status worker for managing load balancer
worker.status.type=status
httpd-vhosts.conf


<VirtualHost *:80>
  DocumentRoot “/local/docroot/htdocs”
  Include conf/tuo.conf
  JkMount /cluster/* loadbalancer
  ...
</VirtualHost>



Step-2: Startup of the JBoss nodes


We have to startup the two JBoss instances that are on the same subnet (to make it simple).

Startup of JBoss node on address 10.3.0.110


./run.sh -c  all  -b  10.3.0.110 -Djboss.messaging.ServerPeerID=1


Startup of JBoss node on address 10.3.0.109


./run.sh -c  all  -b  10.3.0.109 -Djboss.messaging.ServerPeerID=2


Wait the two nodes start communicating each other


11:52:10,764 INFO  [DefaultPartition] New cluster view for partition DefaultPartition (id: 1, delta: 1) : [10.3.0.110:1099, 10.3.0.109:1099]
11:52:10,766 INFO  [DefaultPartition] I am (10.3.0.110:1099) received membershipChanged event:
11:52:10,767 INFO  [DefaultPartition] Dead members: 0 ([])
11:52:10,767 INFO  [DefaultPartition] New Members : 1 ([10.3.0.109:1099])
11:52:10,767 INFO  [DefaultPartition] All Members : 2 ([10.3.0.110:1099, 10.3.0.109:1099])
11:52:10,890 INFO  [RPCManagerImpl] Received new cluster view: [10.3.0.110:53063|1] [10.3.0.110:53063, 10.3.0.109:1102]
11:52:22,027 INFO  [GroupMember] org.jboss.messaging.core.impl.postoffice.GroupMember$ControlMembershipListener@4d16c81d got new view [10.3.0.110:53063|1] [10.3.0.110:53063, 10.3.0.109:1102], old view is [10.3.0.110:53063|0] [10.3.0.110:53063]
11:52:22,028 INFO  [GroupMember] I am (10.3.0.110:53063)
11:52:22,028 INFO  [GroupMember] New Members : 1 ([10.3.0.109:1102])
11:52:22,028 INFO  [GroupMember] All Members : 2 ([10.3.0.110:53063, 10.3.0.109:1102])

As you can see the two instances has found each other and communicates with JGroup protocol. Afterwards this communication the cluster consists of two instances: Received new cluster view: [10.3.0.110:53063|1] [10.3.0.110:53063,10.3.0.109:1102].

Step-3: Write the Statefull Session Bean Clustered


We have to write a simple Session Stateful Bean that uses an instance variable called count. This counter is incremented on each call to the enterprise method.

CounterServiceRemote.java
...

import javax.ejb.Remote;

@Remote
public interface CounterServiceRemote {
 public void increase();
}


CounterService.java
....

import javax.ejb.Stateful;

import org.jboss.ejb3.annotation.Clustered;

/**
 * Session Bean implementation class CounterService
 */
@Stateful
@Clustered
public class CounterService implements CounterServiceRemote{

    private int count=0;
    
    public CounterService() {
       
    }
    public void increase(){
     System.out.println("counter:"+count);
     count++;
     System.out.println("counter:"+count);
    }

}



Step-4: Deploy the jar containing the EJB in both the JBoss nodes


You have to put the jar into the directory <JBOSS-NODE>/server/all/deploy.

Step-5: Wait the deploy to be completed


Console Output

12:33:50,115 INFO  [JndiSessionRegistrarBase] Binding the following Entries in G
lobal JNDI:

        CounterService/remote - EJB3.x Default Remote Business Interface
        CounterService/remote-com.clusterproject.CounterServiceRemote - EJB3.x R
emote Business Interface

12:33:50,205 INFO  [PlatformMBeanServerRegistration] JBossCache MBeans were succ
essfully registered to the platform mbean server.
12:33:50,225 INFO  [STDOUT]


Step-6: Verify in the JMX console that the Stateful Session Bean is registered



Step-7: Verify in the JMX console that the cluster is online



Step-8: Run the EJB client


ClientEJB.java

....

import javax.naming.NamingException;
import com.clusterproject.CounterServiceRemote;

public class ClientEJB {

   public static void main(String[] args) {
 try {
  Object obj =           
ServiceLocator.getInitialContext().lookup("CounterService/remote");
  CounterServiceRemote counterServiceRemote =(CounterServiceRemote) obj;
  counterServiceRemote.incrementa();
  Thread.sleep(10000);
  counterServiceRemote.incrementa();
  Thread.sleep(10000);
  counterServiceRemote.incrementa();
 } catch (NamingException e) {
  e.printStackTrace();
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
  } 
}


ServiceLocator.java
....

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class ServiceLocator {

 public static Context getInitialContext(){
  Properties properties =new Properties();
  properties.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory");
  properties.put(Context.PROVIDER_URL, "jnp://counter.cluster.com:1099");
  InitialContext jndiContext=null;
  try {
   jndiContext=new InitialContext(properties);
  } catch (NamingException e) {
   e.printStackTrace();
  }
  return jndiContext;
 }
}

It's important to notice that:
  • The instruction Thread.sleep(10000) is useful to wait the simulated fault of JBoss node;
  • The address counter.cluster.com is the jnp address of the load balancer machine. The EJB client has to know the address of the balancer machine.
After running the EJB client the below message will be written by the JBoss node that will serve the request.

INFO  [STDOUT] counter:0
INFO  [STDOUT] counter:1



Step-9:Shutdown of the serving JBoss node


During the waiting caused by Thread.sleep(10000), we have to shutdown the serving JBoss node. In this example I have used the JBoss instance on 10.3.0.110.

After the shutdown completes, the active JBoss node will continue serving the request. As you can see in the following console lines:
  • Dead members: 1 ([10.3.0.110:35429])
  • All Members : 1 ([10.3.0.109:1148])

Output

12:38:36,377 INFO  [RPCManagerImpl] Received new cluster view: [10.3.0.110:35429 |6] [10.3.0.109:1148]
12:38:36,577 INFO  [GroupMember] org.jboss.messaging.core.impl.postoffice.GroupM
ember$ControlMembershipListener@191a87d got new view [10.3.0.109:1148|10] [10.3.0.109:1148], old view is [10.3.0.109:1148|9] [10.3.0.109:1148, 10.3.0.110:35429]
12:38:36,577 INFO  [GroupMember] I am (10.3.0.109:1148)
12:38:36,587 INFO  [GroupMember] Dead members: 1 ([10.3.0.110:35429])
12:38:36,587 INFO  [GroupMember] All Members : 1 ([10.3.0.109:1148])
12:38:37,779 INFO  [RPCManagerImpl] Received new cluster view: [10.3.0.109:1148|
10] [10.3.0.109:1148]
12:38:37,849 INFO  [DefaultPartition] New cluster view for partition DefaultPart
ition (id: 10, delta: -1) : [10.3.0.109:1099]
12:38:37,849 INFO  [DefaultPartition] I am (10.3.0.109:1099) received membership
Changed event:
12:38:37,849 INFO  [DefaultPartition] Dead members: 1 ([10.3.0.110:1099])
12:38:37,849 INFO  [DefaultPartition] New Members : 0 ([])
12:38:37,859 INFO  [DefaultPartition] All Members : 1 ([10.3.0.109:1099])
12:38:39,211 INFO  [STDOUT] counter:1
12:38:39,211 INFO  [STDOUT] counter:2
12:38:49,245 INFO  [STDOUT] counter:2
12:38:49,245 INFO  [STDOUT] counter:3



In summary this is only a simple rough example of a Clustered Session Bean, but I hope this post has arouse your curiosity about this subject.

No comments :

Post a Comment

Suggested Pages