Java 2 Ada

Integration of Ada Web Server behind an Apache Server

By stephane.carrez

When you run several web applications implemented in various languages (php, Java, Ada), you end up with some integration issue. The PHP application runs within an Apache Server, the Java application must runs in a Java web server (Tomcat, Jetty), and the Ada application executes within the Ada Web Server. Each of these web servers need a distinct listening port or distinct IP address. Integration of several web servers on the same host, is often done by using a front-end server that handles all incomming requests and dispatches them if necessary to other web servers.

In this article I describe the way I have integrated the Ada Web Server. The Apache Server is the front-end server that serves the PHP files as well as the static files and it redirects some requests to the Ada Web Server.

Virtual host definition

The Apache Server can run more than one web site on a single machine. The Virtual hosts can be IP-based or name-based. We will use the later because it provides a greater scalability. The virtual host definition is bound to the server IP address and the listening port.

<VirtualHost *:80>
  ServerAdmin webmaster@localhost
  ServerAlias demo.vacs.fr
  ServerName demo.vacs.fr
...
  LogLevel warn
  ErrorLog /var/log/apache2/demo-error.log
  CustomLog /var/log/apache2/demo-access.log combined
</VirtualHost>

The ServerName part is matched against the Host: request header that is received by the Apache server.

The ErrorLog and CustomLog are not part of the virtual hosts definition but they allow to use dedicated logs which is useful for trouble shotting issues.

Setting up the proxy

The Apache mod_proxy module must be enabled. This is the module that will redirect the incomming requests to the Ada Web Server.

  <Proxy *>
    AddDefaultCharset off
    Order allow,deny
    Allow from all
  </Proxy>

Redirection rules

The Apache mod_rewrite module must be enabled.

  RewriteEngine On

A first set of rewriting rules will redirect the request to dynamic pages to the Ada Web Server. The [P] flag activates the proxy and redirects the request. The Ada Web Server is running on the same host but is using port 8080.

  # Let AWS serve the dynamic HTML pages.
  RewriteRule ^/demo/(.*).html$ http://localhost:8080/demo/$1.html [P]
  RewriteRule ^/demo/auth/(.*)$ http://localhost:8080/demo/auth/$1 [P]
  RewriteRule ^/demo/statistics.xml$ http://localhost:8080/demo/statistics.xml [P]

When the request is redirected, the mod_proxy will add a set of headers that can be used within AWS if necessary.

Via: 1.1 demo.vacs.fr
X-Forwarded-For: 31.39.214.181
X-Forwarded-Host: demo.vacs.fr
X-Forwarded-Server: demo.vacs.fr

The X-Forwarded-For: header indicates the IP address of client.

Static files

Static files like images, CSS and javascript files can be served by the Apache front-end server. This is faster than proxying these requests to the Ada Web Server. At the same time we can setup some expiration and cache headers sent in the response (Expires: and Cache-Control: respectively). The definition below only deal with images that are accessed from the /demo/images/ URL component. The Alias directive tells you how to map the URL to the directory on the file system that holds the files.

  Alias /demo/images/ "/home/htdocs.demo/web/images/"
  <Directory "/home/htdocs.demo/web/images/">
    Options -Indexes +FollowSymLinks

    # Do not check for .htaccess (perf. improvement)
    AllowOverride None
    Order allow,deny
    allow from all
                                                 
    # enable expirations
    ExpiresActive On
                                  
    # Activate the browser caching
    # (CSS, images and scripts should not change)
    ExpiresByType image/png A1296000
    ExpiresByType image/gif A1296000
    ExpiresByType image/jpg A1296000
  </Directory>

This kind of definition is repeated for each set of static files (javascript and css).

Proxy Overhead

The proxy adds a small overhead that you can measure by using the Apache Benchmark tool. A first run is done on AWS and another on Apache.

ab -n 1000 http://localhost:8080/demo/compute.html
ab -n 1000 http://demo.vacs.fr/demo/compute.html

The overhead will depend on the application and the page being served. On this machine, the AWS server can process arround 720 requests/sec and this is reduced to 550 requests/sec through the Apache front-end (23% decrease).

Boost your php web site by installing eAccelerator

By stephane.carrez 1 comment

This article explains how to boost the performance of a PHP site by installing a PHP accelerator software.

Why is PHP slow

PHP is an interpreted language that requires to parse the PHP files for each request received by the server. With a compiled language such as Java or Ada, this long and error prone process is done beforehand. Even if the PHP interpretor is optimized, this parsing step can be long. The situation is worse when you use a framework (Symfony, CakePHP,...) that requires many PHP files to be scanned.

eAccelerator to the rescue

eAccelerator is a module that reduces this performance issue by introducing a shared cache for the PHP pre-compiled files. The module somehow compiles the PHP files in some internal compiled state and makes this available to the apache2 processes through a shared memory segment.

Installing eAccelerator

First get eAccelerator sources at http://eaccelerator.net/

Then extract the tar.bz2 file on your server:

$ tar xvjf eaccelerator-0.9.6.1.tar.bz2
eaccelerator-0.9.6.1/
eaccelerator-0.9.6.1/COPYING
...

Build eAccelerator module

Before building the module you must first run the phpize command to prepare the module before compilation:

$ cd eaccelerator-0.9.6.1/
$ phpize

Then, launch the configure script:

$ ./configure --enable-eaccelerator=shared \
    --with-php-config=/usr/bin/php-config

Finally build the module:

$ make

Install eAccelerator

Installation is done by the next steps:

$ sudo make install

Don't forget to copy the configuration file (have a look at its content but in most cases it works as is):

$ sudo cp eaccelerator.ini  /etc/php5/conf.d/

Restart Apache server

To make the module available, you have to restart the Apache server:

$ sudo /etc/init.d/apache2 restart

Performance improvements

What performance gain can you expect... That will depend on the PHP software and the page. It's easy to have an idea.

To measure the performance improvement, you can use the Apache benchmarking tool. Do a performance measurement on the web site before the installation and another one after. Be sure to benchmark the same page.

The following command will benchmark the http://mysite.mydomain.com/index.php page 100 times with only one connection.

$ ab -n 100 http://mysite.mydomain.com/index.php

Below is an extract of the percentage of the requests served within a certain time (ms) for one of my web page served by Dotclear:

         Without        with
        eAccelerator  eAccelerator
 50%       383           236
 66%       384           237
 75%       387           238
 80%       388           239
 90%       393           258
 95%       425           265
 98%       536           295
 99%       796           307
100%       796           307 (longest request)

The gain varies from 38% to 60% so it is quite interesting. The other benefit is that the variance is also smaller meaning that requests are served globally in the same time.

1 comment
To add a comment, you must be connected. Login to add a comment

Apache and JBoss integration with mod_rewrite and mod_proxy

By stephane.carrez 1 comment

The problem I wanted to solve was to be able to use the Apache URL rewrite before forwarding the request to a JBoss server. The Apache and JBoss were already integrated with mod_jk (Deploying a J2EE application behind an Apache server). The URL rewriting rule does not work in that case (at least I was not able to make it work). I investigated the Apache mod_rewrite and its proxy configuration.

First, in your Apache host configuration you have to enable the Apache rewrite module. This is done by the RewriteEngine directive. To make sure that the server name is propagated to JBoss, you have to use the ProxyPreserveHost directive. If you don't do this, JBoss will receive 'localhost' as server name (ie, the servlet request getServerName() method will not return what you expect). You then define your rewrite rule and use the proxy forwarding mode indicated by [P].

 <VirtualHost *:80>
    RewriteEngine On
    ProxyPreserveHost On
    RewriteRule ^/some-path/(.*)$  \
           http://localhost:8080/web-app-deploy-name/$1 \[P\]
   ...
  </VirtualHost>

You have to make sure the Apache modules are available, for this execute these commands:

  sudo a2enmod proxy proxy_http rewrite

Once your configuration files are ready, reload Apache configuration:

  sudo /etc/init.d/apache2 reload

That it!

This mod_rewrite and mod_proxy configuration is very powerful and easier to setup than mod_jk.

1 comment
To add a comment, you must be connected. Login to add a comment

Transparent Web server migration with Apache proxy

By stephane.carrez 1 comment

Once your new server is ready, you'll need to update your DNS so that users switch to your new server. The DNS change may take some time to propagate: this is controlled by the DNS TTL (time to live). You could set the TTL to 1 minute (like gmail) but this will increase the traffic to your DNS. Sometimes you don't control the TTL or you must do the IP switch when a previous TTL was very high (for example one day). In those cases, several users will continue to use the old server until the TTL ellapsed.

You can use the Apache proxy module on the old server to redirect the traffic to the new server during the DNS transition.

The configuration extract below is suitable for a Web server with a secure connection (https). On the old server, update your Apache server configuration and use:

 <VirtualHost *:443>
      ProxyRequests Off
      <Proxy *>
            Order allow,deny
            Allow from all
      </Proxy>
      ProxyVia On
      ProxyPreserveHost On
      ProxyPass / https://91.121.nnn.nnn/
      ProxyDomain mydomain.com
 </VirtualHost>

where you'll replace the 91.121.nnn.nnn IP address by your new server IP address, and the mydomain.com by your own domain.

You must also make sure that the Apache mod_proxy modules are installed. For this, run:

 sudo a2enmod proxy proxy_http

Once this is ok, you have to reload the Apache configuration:

 sudo /etc/init.d/apache reload

And of course test your old server and watch that the proxy works correcty!

1 comment
To add a comment, you must be connected. Login to add a comment

Deploying a J2EE application behind an Apache server in a production environment

By stephane.carrez

In a production environment, you should not put your JBoss application as a Web front-end. Instead, you should use an Apache server and configure it to redirect specific Web application requests to your J2EE server. There are many many advantages in doing this:

  • The Apache server can serve static files (CSS, images, javascript files) faster than JBoss/Tomcat.
  • When you need it, you can activate SSL on Apache without having to change your application.
  • The Apache SSL implementation is faster compared to the Tomcat implementation (and a lot easier to configure!).
  • You can have a better control of HTTP headers. No need to develop any servlet filter for that.
  • You can get compression out of the box. No need to develop another servlet filter either (no need to configure Tomcat connector either!).

I assume here that the Apache server is already installed with the following modules and these modules are enabled.

jk headers expires ssl deflate rewrite

If they are not enabled, you can enable them using the command:

sudo a2enmod jk

Step 1: Explode your Web or J2EE application

For Apache to serve the static files, it is necessary to have those files available in a directory that the Apache server can access. For this, explode your J2EE application (EAR file) and all the Web applications which have static files to be served by Apache. You will do this in a directory somewhere with one of the command:

mkdir ''myapplication''
cd myapplication && jar xf ../''myapplication''.ear
mv ''mywebapp''.war ''mywebapp''-new.war && mkdir ''mywebapp.war''
cd ''mywebapp''.war && jar xf ../''mywebapp''-new.war

If your EAR file contains a WAR file, you have to explode it as well (the static files to be served are there!). It is also a good practice to explode it in a directory having the same name as the WAR. Once everything is exploded, you can also configure JBoss to directly use the exploded directory (this will speed up JBoss startup significantly).

Step 2: Create a site configuration file

For good practices, you should write a configuration file that corresponds to the site that you are going to manage. This allows to enable or not a server configuration which will be useful during the maintenance. For this, create a file in /etc/apache2/sites-available and put an initial content (replace myserver.mydomain.com with your server name and server-installation-dir with the path of your installation directory):

 <VirtualHost _default_:80>
       ServerAdmin webmaster@localhost
       ServerAlias ''myserver.mydomain''.com
       ServerName ''myserver.mydomain''.com
       DocumentRoot /''server-installation-dir''
       <Directory />
               Options FollowSymLinks
               AllowOverride None
       </Directory>
       ErrorLog /var/log/apache2/''myserver''-error.log
       LogLevel warn
       CustomLog /var/log/apache2/''myserver''-access.log combined
 </VirtualHost>

The server-installation-dir should point to the WAR exploded directory.

It is also a good practice to use a specific log file for each server (virtual host) that you configure in Apache. Restrict the number of options to the minimum so that you do not activate an option that could compromise the security and also to keep the configuration understandable and manageable.

You may find additional information about virtual hosts on Apache Virtual Host documentation.

Step 3: Configure Apache mod_jk

The Apache server can redirect requests to JBoss/Tomcat by using the mod_jk module (jk). Edit the file /etc/apache2/mods-available/jk.load and define the following properties:

 
JkWorkersFile /etc/apache2/worker.properties
JkShmFile     /var/log/apache2/mod_jk.shm
JkLogFile     /var/log/apache2/mod_jk.log
JkLogLevel    info
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

Write a /etc/apache2/worker.properties file with the following example (be sure to replace some of the paths):

workers.tomcat_home=''directory where Tomcat is installed''
workers.java_home=''directory of the JDK installation home'' 
ps=/
worker.list=workerName
worker.workerName.port=8009
worker.workerName.host=localhost
worker.workerName.type=ajp13
worker.wokerName.lbfactor=1

The workerName is the name you are going to use within your site configuration file to tell Apache to which JBoss/Tomcat the requests are going to be forwarded.

You may find additional information on The Apache Tomcat Connector - Webserver HowTo

Step 4: Configure mod_jk in your site configuration file

Now that mod_jk is configured, you have to setup your site configuration file to redirect some of your URLs to your JBoss/Tomcat server through the AJP connector. This is done by the JkMount and JkUnMount directives. For this, add the following lines:

<VirtualHost _default_:80>
 ....
 JkMount /''mywebapp''/* ''workerName''
 JkOptions +ForwardURICompat
</VirtualHost>

where mywebap is the Web application context of your Web application when it is running. All request to /mywebapp will be redirected to JBoss/Tomcat. If Apache has to serve static files located in the same context, you have to use:

   JkUnMount /mywebapp/*.css workerName
   JkUnMount /mywebapp/*.js workerName
   JkUnMount /mywebapp/*.html workerName
   JkUnMount /mywebapp/*.png workerName

The Javascript, CSS, images and HTML files will not be served by JBoss/Tomcat because the JK connector is not activated for these links.

You could also only mount the dynamic files that your Web application is serving (like *.jsp, *.do, *.jsf or *.seam). This may not be the best solution if your Web application has specific servlets that are mapped without any extension (like an XMLRPC servlet, a Seam resource servlet and others). This is why, it is best to mount everything to your Web application and then manually specify what is static and served by Apache directly. This will prevent you from big surprises!

Step 5: Configure caching and compression

Compression can be activated easily by adding the following line at the top of your site configuration file (see the deflate module):

       AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript

Browser caching is activated and controlled by the expires module in Apache. You may add the following options within the <Directory> section controlling the static files.

       # enable expirations
       ExpiresActive On
       # Activate the browser caching (CSS, images and scripts should not
       # change)
       ExpiresByType text/css A1296000
       ExpiresByType image/png A1296000
       ExpiresByType image/gif A1296000
       ExpiresByType image/jpg A1296000
       ExpiresByType text/javascript A1296000

You may find additional information on Apache Module mod_expires and Apache Module mod_deflate.

Step 6: Harden you Apache configuration

JBoss can add headers in the HTTP response. The X-Powered-By header exposes what implementation is behind your site. This header is created by a servlet filter that is activated by default in JBoss web configuration files (server/default/deploy/jbossweb-tomcat55.sar/conf/web.xml). You can either disable this filter by commenting the following lines:

  <!-- <filter-mapping>
     <filter-name>CommonHeadersFilter</filter-name>
     <url-pattern>/*</url-pattern>
  </filter-mapping> -->

If you cannot change this, don't worry! The Apache server can remove those headers for you. Just add the following directives in your site configuration file:

   # For security reasons, do not expose who serves the page
   <LocationMatch '^/''mywebap''/.*'>
       Header unset 'X-Powered-By'
   </LocationMatch>

Removing this header is also good for performance as it reduces the size of responses. You may have a look at the Apache documentation Apache Module mod_headers.

By default, the Apache server sends a complete signature in the Server header response. You should verify the /etc/apache2/apache2.conf file and make sure you have the following options:

ServerSignature Off
ServerTokens Prod

To verify that your server generates the good response headers, you may use the wget -S command or Firebug to look at those headers.