Search This Blog

Friday, March 11, 2011

Custom HTTP headers with XmlRpcClient

Recently I encountered the problem with apache xml-rpc client when I needed to set some custom request headers with each request. Since I didn't find any examples on google how to do it here is an example.


For this to work you need to implement XmlRpcTransport and override initHttpHeaders method.


XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
        config.setServerURL(new URL("http", "localhost", 9000, ""));
               
        config.setEnabledForExtensions(true);
        config.setContentLengthOptional(false);
        
        client = new XmlRpcClient();
        client.setConfig(config);
        //implement transport factory
        XmlRpcTransportFactory xmlRpcTransportFactory = new XmlRpcCommonsTransportFactory(client) {

            @Override
            public XmlRpcTransport getTransport() {

                return new XmlRpcCommonsTransport(this) {

                    @Override
                    protected void initHttpHeaders(XmlRpcRequest pRequest) throws XmlRpcClientException {
                        super.initHttpHeaders(pRequest);
                        //add custom header
                        super.method.addRequestHeader("My-Header", "some header");
                    }
                };
            }
        };
        client.setTransportFactory(xmlRpcTransportFactory);
        
        
        factory = new ClientFactory(client);
        
        //actual method call
        IMyInterface myHandler = (IMyInterface)factory.newInstance(IMyInterface.class);
        myHandler.testHeaderMethod("test");
For every remote method call, header named My-header will be set to http request.

Sunday, March 6, 2011

Tomcat load balancing

I will demonstrate tomcat load balancing by connecting apache web server and tomcat application servers through ajp connector.

In this example I use mod_proxy and mod_proxy_ajp which is built into Apache web server from versions 2.2 and up. AJP stands for Apache Jserv Protocol wich is binary packet-oriented protocol and apache web server communicates with tomcat over TCP connections, maintains persistent connections and reuses a connection for multiple request/response cycles. 

By having apache web server in front of multiple tomcat application servers you can achive to handle more concurent requests and also have some sort of failover in case one of the nodes breaks down.
Good thing is also that you can serve your static content like images, javascript and css files from apache web server(apache web server is somewhat faster in serving static content than tomcat), and let the tomcat application server to handle the real applcation processing.

For example purposes I'll install two tomcat application servers and apache web server on the same physical machine but in the real world that would probably be separate machines.

1. First, I install two separate instances of tomcat application servers(download and unzip). You will need to change the ports in server.xml config file to avoid port conflicts.
Tomcat A
<Server port="8005" shutdown="SHUTDOWN"> 
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatA">
Tomcat B
<Server port="8006" shutdown="SHUTDOWN"> 
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatB">
Last line has attribute jvmRoute and is concerned to session stickyness explained further bellow.
For test purposes deploy some dummy application under /balance context.
Try to start both instances to see if everything is ok.

2. Install apache web server. I am using Ubuntu:
sudo apt-get install apache2

3. Edit virtual host configuration file.
On Ubuntu, I edit /etc/apache2/sites-enabled/000-default.

<Proxy balancer://tomcatservers>
    BalancerMember ajp://localhost:8009 route=tomcatA loadfactor=1
        BalancerMember ajp://localhost:8010 route=tomcatB loadfactor=2
    </Proxy>

    <Location /balancetest>
    Allow From All
        ProxyPass balancer://tomcatservers/balance stickysession=JSESSIONID nofailover=off
    </Location>
We defined load balancer for two nodes (tomcatA and tomcatB) and proxy pass for load balancer. In proxy pass we defined that all request going to /balancetest are proxied to tomcat application server to /balance context. Entering url http://localhost/balancetest will display jsp page or whatever either from tomcatA or tomcatB depending on load balancer.

The load balancer supports session stickyness wich means that all request for some session identifier are proxied to same tomcat web application instance in cluster that first served the client. To track this information tomcat adds jvmRoute information that uniquely identifies tomcat application server in the cluster.

So, jsessionid cookie would look something like this:
jsessionid=4C87288CB0CDB71D0DCA412E178A1480.tomcatB

Apache web server will compare route  value defined in balancer with cookie value to proxy request to tomcat instance that maintains corresponding session.
It is important that jvmRoute attribute in server.xml config file and route attribute in virtual host config file are identical for some tomcat instance.

Load factor attribute allows to distribute load between nodes. If load factors are equal, load is equally distributed. In our case tomcatB will recieve two times more requests than tomcatA.

4. And for this to work following modules must be enabled on apache server.
  • proxy
  • proxy_balancer
  • proxy_ajp
On Ubuntu, you can do this from console by typing a2enmod command.
a2enmod proxy
a2enmod proxy_balancer
a2enmod proxy_ajp


Restart apache and tomcat servers and you'll have load balancing set up with minimal configuration.