Wednesday, March 9, 2011

Configurating Apache to communicate with Apache Tomcat

Hey...

Most of the servers have more than one running application on their different ports. You should select the port to visit the application. For example: http://213.248.89.10:7070. The default port of http is 80. I mean if you request http://213.248.89.10, the request is http://213.248.89.10:80. we can have more than one domain on a server but only one application can be run on specified port. So how can we run more than one application and access them without determining any port? The solution is: we can run an web server application on the default port and this web server application should choose the other running application and forward the request to it by requested address. For example a server with ip address of 213.248.89.10 has this applications:

Application Nameportaddress
A7070a.com
B6060b.com
C5050c.com

we should run an web server application on port 80. Then it should process the request address:

if (requestAddress is a.com) then communicate with port 7070
else if (requestAddress is b.com) then communicate with port 6060
else if (requestAddress is c.com) then communicate with port 5050

Here I am going to do this sample by Apache and Apache tomcat. My sample server(213.248.89.10 IP address) has these running applications:

Application Nameportajp portaddressWeb Server
-80-
Apache
A70708007a.comApache Tomcat
B60608006b.comApache Tomcat
C50508005c.comApache Tomcat

The connection protocol between Apache and Apache tomcat is ajp13(Apache JServe Protocol). So what is ajp13?

ajp13

The ajp13 protocol is packet-oriented. A binary format was presumably chosen over the more readable plain text for reasons of performance. The web server communicates with the servlet container over TCP connections. To cut down on the expensive process of socket creation, the web server will attempt to maintain persistent TCP connections to the servlet container, and to reuse a connection for multiple request/response cycles.

Once a connection is assigned to a particular request, it will not be used for any others until the request-handling cycle has terminated. In other words, requests are not multiplexed over connections. This makes for much simpler code at either end of the connection, although it does cause more connections to be open at once.

Once the web server has opened a connection to the servlet container, the connection can be in one of the following states:

  • Idle
    No request is being handled over this connection.
  • Assigned
    The connecton is handling a specific request.
Once a connection is assigned to handle a particular request, the basic request informaton (e.g. HTTP headers, etc) is sent over the connection in a highly condensed form (e.g. common strings are encoded as integers). Details of that format are below in Request Packet Structure. If there is a body to the request (content-length > 0), that is sent in a separate packet immediately after.

At this point, the servlet container is presumably ready to start processing the request. As it does so, it can send the following messages back to the web server:

  • SEND_HEADERS
    Send a set of headers back to the browser.
  • SEND_BODY_CHUNK
    Send a chunk of body data back to the browser.
  • GET_BODY_CHUNK
    Get further data from the request if it hasn't all been transferred yet. This is necessary because the packets have a fixed maximum size and arbitrary amounts of data can be included the body of a request (for uploaded files, for example). (Note: this is unrelated to HTTP chunked tranfer).
  • END_RESPONSE
    Finish the request-handling cycle.
Each message is accompanied by a differently formatted packet of data. Here you can See Response Packet Structures for details.

Well...
So in my solution any requests should first pass Apache then visit the specified Apache Tomcat
image 1
Now it's the time to overview the steps of configuration.

Apache Configuration
I should first configure the Apache to map the request to specified tomcat node.
First I should copy mod_jk.so file to the [Apache root path]/modules path. you can download mod_jk.so here.
Then I should create [Apache root path]conf/workers.properties file and write ajp port specification in it.
# Define 1 real worker named ajp13
worker.list=ajp7,ajp6,ajp5

# Set properties for worker named ajp13 to use ajp13 protocol,
# and run on port 8009
worker.ajp7.type=ajp13
worker.ajp7.host=213.248.89.10
worker.ajp7.port=8007
worker.ajp7.lbfactor=50
worker.ajp7.cachesize=10
worker.ajp7.cache_timeout=600
worker.ajp7.socket_keepalive=1
worker.ajp7.socket_timeout=300

worker.ajp6.type=ajp13
worker.ajp6.host=213.248.89.10
worker.ajp6.port=8006
worker.ajp6.lbfactor=50
worker.ajp6.cachesize=10
worker.ajp6.cache_timeout=600
worker.ajp6.socket_keepalive=1
worker.ajp6.socket_timeout=300

worker.ajp5.type=ajp13
worker.ajp5.host=213.248.89.10
worker.ajp5.port=8005
worker.ajp5.lbfactor=50
worker.ajp5.cachesize=10
worker.ajp5.cache_timeout=600
worker.ajp5.socket_keepalive=1
worker.ajp5.socket_timeout=300
Then I should create [Apache root path]/conf/extra/mod_jk.conf file. This file is to determine the virtual hosts specifications like this:
# Load mod_jk module
# Update this path to match your modules location
LoadModule jk_module modules\mod_jk.so

# Where to find workers.properties
# Update this path to match your conf directory location
JkWorkersFile conf\workers.properties

# Where to put jk logs
# Update this path to match your logs directory location
#JkLogFile c:/tomcat/logs/mod_jk.log

# Set the jk log level [debug/error/info]
JkLogLevel info

# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"

# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"

<VirtualHost a.com>
# Send everything for context a.com to worker ajp7
JkMount / ajp7
JkMount /* ajp7
</VirtualHost>

<VirtualHost b.com>
# Send everything for context b.com to worker ajp6
JkMount / ajp6
JkMount /* ajp6
</VirtualHost>

<VirtualHost b.com>
# Send everything for context c.com to worker ajp5
JkMount / ajp5
JkMount /* ajp5
</VirtualHost>
Then I should open [Apache root path]/conf/httpd.conf file and include mod_jk.conf file (the file I have created in above) in httpd.conf file:
#
# This is the main Apache HTTP server configuration file.  It contains the
# configuration directives that give the server its instructions.
...
# Various default settings
#Include conf/extra/httpd-default.conf

Include conf/extra/mod_jk.conf
# Secure (SSL/TLS) connections
#Include conf/extra/httpd-ssl.conf
#
# Note: The following must must be present to support
#       starting without SSL on platforms with no /dev/random equivalent
#       but a statically compiled-in mod_ssl.
#
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

Apache Tomcat configuration
I should just open [Apache Tomcat root]/conf/server.xml file and change the ajp connector tag:
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JasperListener" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
  <Service name="Catalina">
    <Connector port="7070" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF8" />
    <Connector port="8007" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
      </Host>
    </Engine>
  </Service>
</Server>

Final step
Just I should restart Apache service and then start Apache Tomcat(s). Now if I request a.com the tomcat on 7070 port will be notified and if I request b.com the tomcat on 6060 port will be notified and so on.

Note: If you test these steps on your client and no domain does not exist, you should define new host name for a.com, b.com and c.com to be mapped to 127.0.0.1 IP. In MS Windows open [windows drive]/Windows/System32/drivers/etc/hosts file then add these lines:
...
127.0.0.1  localhost
127.0.0.1  a.com
 
127.0.0.1  b.com
127.0.0.1  c.com

...

Enjoy yourselves ...

all rights reserved by Mostafa Rastgar and Programmer Assistant weblog

No comments: