Category: Config


phương pháp sau sẽ làm giảm đỉ số lượng Bandwidth phải sử dụng với web của bạn và tăng tốc đọ truy cập web của nguòi dùng

các bạn đều biết phần lớn các file images, css, javascript, files loại khác đèu có thể làm cho chặt chẽ cáu hình và sủ dụng để tăng tóc download với nguòi sủ dụng. hơn nữa nó có thể báo cho trình duyệt của người dùng luu trữ lại cho dùng lần sau bằng cách thông báo thời gian sủa lại hoạc câp nhật đẻ không tốn thời gian và luu lượng trao đổi khi ghé thăm web..

sau đây là mã số của thời gian báo cho trình duyệt bạn có thể sủ dụng

Trích dẫn:
#===================================================================== ========#
# TIME CHEAT SHEET
#===================================================================== ========#
# 300 5 M # 604800 1 W
# 2700 45 M # 1814400 3 W
# 3600 1 H # 2419200 1 M
# 54000 15 H # 14515200 6 M
# 86400 1 D # 26611200 11 M
# 518400 6 D # 29030400 1 Y (never expire)

còn đây là hai giải pháp cho việc này
sử dụng Apache Module mod_expires
ví dụ

Mã:
ExpiresActive On
ExpiresDefault A300
ExpiresByType image/x-icon A2592000
ExpiresByType application/x-javascript A2592000
ExpiresByType text/css A2592000
ExpiresByType image/gif A604800
ExpiresByType image/png A604800
ExpiresByType image/jpeg A604800
ExpiresByType text/plain A604800
ExpiresByType application/x-shockwave-flash A604800
ExpiresByType video/x-flv A604800
ExpiresByType application/pdf A604800
ExpiresByType text/html A300

cách hai sử dụng mod_headers thêm vào header của các html — cách này khá dễ và ưa chuộng
ví dụ

Mã:
# htm files are php
AddHandler application/x-httpd-php .php .htm 

# setup errordocuments to local php file
ErrorDocument 404 /cgi-bin/error.htm
ErrorDocument 403 /cgi-bin/error.htm
ErrorDocument 500 /cgi-bin/error.htm

# Turn on Expires and set default to 0
ExpiresActive On
ExpiresDefault A0

# Set up caching on media files for 1 year (forever?)
<FilesMatch “\.(ico|flv|pdf|mov|mp3|wmv|ppt)$”>
ExpiresDefault A29030400
Header append Cache-Control “public”
</FilesMatch>

# Set up caching on media files for 1 week
<FilesMatch “\.(gif|jpg|jpeg|png|swf)$”>
ExpiresDefault A604800
Header append Cache-Control “public, proxy-revalidate”
</FilesMatch>

# Set up 2 Hour caching on commonly updated files
<FilesMatch “\.(xml|txt|html|js|css)$”>
ExpiresDefault A7200
Header append Cache-Control “private, proxy-revalidate, must-revalidate”
</FilesMatch>

# Force no caching for dynamic files
<FilesMatch “\.(php|cgi|pl|htm)$”>
ExpiresDefault A0
Header set Cache-Control “no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, no-transform”
Header set Pragma “no-cache”
</FilesMatch>

Nguồn xã luận

Redis vs Memcached

This month’s Linux Journal has an article about Redis.  I read about it while sitting on the shitter, because that’s about all that Linux Journal is useful for.  The article itself was crap, but the introduction to this product was at least tempting.

The basics:  take memcached and add a disk backing, replication, virtual memory, and some cool additional data structures.  Hashes, lists, sets, sorting, joining, transactions, yay!  I instantly got a geek boner scanning the feature list.  My boner quickly faded to about half mast when I saw the C clients that are available.  No consistent hashing?  Wait, no server hashing at all? Yet they have a gay Ruby client, fully featured?  C is the lowest common denominator when it comes to language support.  Start there, then add high level bindings using this low level library.  libmemcached got it right.  You start on the bottom and work your way up to higher level bindings.  It seems that Redis took the wrong approach to this which will result in every language having a different client implementation.  This leads to inconsistencies between languages, which is bad news.  That on first glance gave me that headed-for-the-toilet feeling for this project.

With a half-mast boner, I decided to do some benchmark comparisons.  Perhaps I’ll see some real numbers that might arouse me.  Stand back, I’m going to do some science!

The Test Setup

I used the same machine as client and server for both tests to avoid network adding a skew to the results.  (This should stress the algorithms themselves)  The hardware itself doesn’t matter, it’s an apples to apples comparison.  The software does however:

  • Redis 2.0.0 rc4
  • Memcached 1.4.5 (release)
  • Credis (libcredis client) 0.2.2
  • Libmemcached 0.31  (I know this is a little outdated, but for this test it works fine)
  • Memcache client benchmark app: mc_stress.c
  • Redis client benchmark app: redis_stress.c

The clients I wrote are just simple iterations of setting and getting keys and timing the results.  Since WP sucks for posting source code snippets I had to stuff these into PDF files.  Feel free to recreate my tests and post your results, since that’s what science is all about.

Results

This test varies the key size with a static value of only 3 bytes.  (Using 100k unique keys)  I’m guessing this will stress the internal hashing algorithms used for processing the key names.  As you can see here, Redis came up short by a range of 20-26% the speed of memcache.

Same test, but showing the Multi-GET performance.  Redis clearly has some kind of problem here, and I believe it might be due to requesting a large amount of keys in a single MGET operation.  100,000 keys seemed to be the upper limit… but in every case, Redis came up short.

This test stresses the different value sizes.  Using a small 10 byte key length, I used varying sizes of value lengths up to around 16KB.  Even though memcached advertises a 1MB value limitation, performance dropssignificantly over 8192 bytes.  SET speed was reduced by 100 times and depending on the test, GET speed was either significantly reduced, or reduced by 100 times as well.  I suspect that playing around with slab sizes might assist with the speed here, but that becomes quite the pain in the ass.  The main point:  If you use memcache, do not stuff large values into a single key.  Hash them out into namespaces, you’ll be much better off.

Adding to this, Redis boasts a 1GB value size.  We would need to graph another relationship of transfer rate to determine if there is an inefficiency in handling larger values, or if it’s just due to the size of the value itself.  Memcache shows a similar trend as value size increases, but again Redis is 20-40% slower than memcache.

Here is the same test, showing Multi-GET performance.  Similar trend as key length test.

Conclusion

After crunching all of these numbers and screwing around with the annoying intricacies of OpenOffice, I’m giving Redis a big thumbs down.  My initial sexual arousal from the feature list is long gone.  Granted, Redis might have its place in a large architecture, but certainly not a replacement to memcache.  When your site is hammering 20,000 keys per second and memcache latency is heavily dependent on delivery times, it makes no business sense to transparently drop in Redis.   The features are neat, and the extra data structures could be used to offload more RDBMS activity… but 20% is just too much to gamble on the heart of your architecture.

Maybe sometime in the future Redis will be up to par with Memcached performance.  Or, it could be the extra VM and disk backing is inherently flawed to begin with.  In that case, Memcached will always win, and Redis will be only useful in a niche market somewhere between DB caching and NoSQL fanboys.

MySQL Connections

MySQL will allow n number of connections at a given point of time, To find out that n no of connections run the following command.

mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 100   |
+-----------------+-------+
1 row in set (0.00 sec)

So this server will allow max of 100 connections at any given point of time.

Before we change the following variable, its necessary to understand how MySQL uses connections and tables. Consider that for every connection on your server, you are querying 3 tables. ie. MySQL will open 3 file descriptors for each connection (1 for each table)

MySQL Doc
…the table is opened independently by each concurrent thread. …

Considering your site has a about 300 connections per/second, 200 of those connections will be dropped since your setting is 100. So changing the setting to 300, MySQL will open (300 x 3 = 900) file descriptors. A file descriptor is nothing but a connection to that file (programmers,sysads will know what I mean). Now its necessary to check what does your OS suuport, normally OS will support about X no. of file descriptors at a given point of time. So increasing your connection settings will not help solve the issue. You need to consult your OS’s manual before changing the settings on a production server.

Fine, Lets assume that every param is under the limit, we can tweak around, As in MySQL almost every parameter can be controlled by configuration file /etc/my.cnf in Linux or [MySQL INSTALL DIR]\my.ini in Windows.

Open the file and add the following under mysqld section max_connections=300. So it should be something like this.

[mysqld]
max_connections=300
...

Restart MySQL Server after changing the file. You can alternatively run a Command while the server is running, but the setting won’t be maintained if the server restarts. The following command can be executed

mysql> set global max_connections=300;

References

  1. Show Variables
  2. Server System Variables
  3. Server Parameters
  4. Too many connections
  5. Table Cache

MySQL Connection Errors

There could be many reasons why a connection to MySQL server can fail, like

  • Networking Problem
  • Server itself could be down
  • Authentication Problems
  • Maximum Connection Errors allowed.

Of all the errors, this thread will discuss Maximum Connection Errors.
This particular parameter max_connect_errors defines the no. of connection errors a particular host can make before it is banned. Yes Banned! This is a feature that MySQL provides to limit erroneous clients.

By default 10 maximum connection errors are allowed per host. You can check using foll. command.

mysql> show variables like 'max_connect_errors';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| max_connect_errors | 10    |
+--------------------+-------+
1 row in set (0.02 sec)

You can set the above variable in the same manner as setting max_connections, ie in my.cnf file.

[mysqld]
max_connect_errors=100
...

Once particular host exceeds the no. of errors, a particular command is required to be given to the server to reset the host connect errors and indirectly allow the hosts to connect to the server. This command flush hosts will help you do that.

In our scenario, where there are 300 connections per/second, and each connection-query is taking some processing time before connection is closed, There could be a time where the Server is busy, and starts to queue processes, in such a case the connection would be still held until its query is processed. So when a new connection is required, the server may reject it considering the connections that are currently active. ie. We’d set max_connections to 300, so every new connection request could be denied adding to the connection errors, which could mean the host could be blocked. Considering a Web Server + MySQL Server scenario, the web server’s host could be banned until a flush hosts is fired at the MySQL Server.

So in such a case, it is necessary to increase the no. of simultaneous connections that can be made to the server by changing the max_connections parameter. Still if the load on MySQL server is high, and you are getting Connection errors, its time to load balance the Server.

Mysql Replication: Same Server, Rewrite database

MySQL support same-server replication into another database, Its quite a weired requirement, but in reality weired is common.

Consider a server 192.168.5.70, which has 2 databases db1 and db2
Now we shall set up replication for two tables on db1, ie. table1 and table2.

Here is the my.cnf

[mysqld]
server-id=1
#### Replication ####
report-host=master-is-slave-host
log-bin=192.168.5.70-binlog
relay-log=192.168.5.70-relaylog

replicate-same-server-id=1

binlog-do-db=db1

# Note.... On rewrite, the  command is changed into buffer
# so the replicate-do-db and replicate-do-table should have the
# re-written db name.
replicate-rewrite-db=db1->db2
replicate-do-table=db2.table1
replicate-do-table=db2.table2

Lets look at it carefully.

replicate-same-server-id=1
This is to tell the slave the commands that it has to execute will be having the same server-id as its own.

binlog-do-db=db1
To log only database db1

replicate-rewrite-db=db1->db2
Let the slave know that whatever command is for db1 it has to execute on db2

replicate-do-table=db2.table1
replicate-do-table=db2.table2

Note how the table in db2 is replicated, as against the general thinking of db1.table1, MySQL could be using some buffered string and then evaluating the replicate-do-table rule.

Mysql Replication: Configuring and Checking the Slave

Once we’ve issued the command start slave;, The Replication should ideally start, But as well all know nothing works initially. So to check everything is working, issue the following command.

mysql> show slave status\G
*************************** 1. row ***************************
             Slave_IO_State: Waiting for master to send event
                Master_Host: 192.168.5.99
                Master_User: repl
                Master_Port: 3306
              Connect_Retry: 60
            Master_Log_File: mysql-bin.000009
        Read_Master_Log_Pos: 37822065
             Relay_Log_File: ruturaj-vartak-relay-bin.000028
              Relay_Log_Pos: 37822202
      Relay_Master_Log_File: mysql-bin.000009
           Slave_IO_Running: Yes
          Slave_SQL_Running: Yes
            Replicate_Do_DB: test
        Replicate_Ignore_DB:
         Replicate_Do_Table:
     Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
                 Last_Errno: 0
                 Last_Error:
               Skip_Counter: 0
        Exec_Master_Log_Pos: 37822065
            Relay_Log_Space: 37822202
            Until_Condition: None
             Until_Log_File:
              Until_Log_Pos: 0
         Master_SSL_Allowed: No
         Master_SSL_CA_File:
         Master_SSL_CA_Path:
            Master_SSL_Cert:
          Master_SSL_Cipher:
             Master_SSL_Key:
      Seconds_Behind_Master: 0
1 row in set (0.00 sec)

Check the parameters

  • Slave_IO_Running (Should be yes)
  • Slave_SQL_Running (Should be yes)

If either of them are No, Open the /hostname.err file. This is the mysql error file, You can check it for a specific configuration error. Most often either the Master log is not present (incorrect filename) or the master log position is incorrect. You can re-specify the parameters by using change master to .. command.

But be sure you issue stop slave first and then change master to … command and finally start slave

Once both the Slave_IO and Slave_SQL parameters are yes, you can start writing, modifying anything in thetest database of the server.

You can also check the hosts connected by issuing this command on the master

mysql> show slave hosts;
+-----------+----------------+------+-------------------+-----------+
| Server_id | Host           | Port | Rpl_recovery_rank | Master_id |
+-----------+----------------+------+-------------------+-----------+
| 1         | slave-server-1 | 3306 | 0                 | 2         |
+-----------+----------------+------+-------------------+-----------+

Mysql Replication: Configuring Slave

Now that master is all set! Its time for the slave to obey the master. To setup the slave, We need to tell the Slave server which log file it is supposed to read, and yet again we need to give a unique server-id to the slave.

So open the /etc/my.cnf and add the following below the [mysqld] section.

server-id=2
replicate-do-db=test
report-host=slave-server-1
  • The directive replicate-do-db: specifies that only test database is to be replicated.
  • The directive report-host: specifies the hostname that will be seen on the master

Restart your MySQL server.

Now we are ready to tell the server about the master server.
Give following command to the slave server.

mysql> CHANGE MASTER TO
    ->     MASTER_HOST='192.168.5.100',
    ->     MASTER_USER='repl',
    ->     MASTER_PASSWORD='slavepass',
    ->     MASTER_LOG_FILE='mysql-bin.000001',
    ->     MASTER_LOG_POS=4;

The master_log_pos is the parameter which tells the server to read the file from a particular position.
You can check the position by the following command on the master server.

mysql> show binlog events in 'mysql-bin.000009' limit 10;
+------------------+------+-------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name         | Pos  | Event_type  | Server_id | End_log_pos | Info                                                               |
+------------------+------+-------------+-----------+-------------+--------------------------------------------------------------------+
| mysql-bin.000009 | 4    | Format_desc | 2         | 98          | Server ver: 5.0.19-standard-log, Binlog ver: 4                     |
| mysql-bin.000009 | 98   | Intvar      | 2         | 126         | INSERT_ID=10077                                                    |
| mysql-bin.000009 | 126  | Query       | 2         | 430         | use `test`; insert into testtable (tname) values('ruturaj vartak') |
| mysql-bin.000009 | 430  | Query       | 2         | 511         | use `test`; truncate table testtable                               |
+------------------+------+-------------+-----------+-------------+--------------------------------------------------------------------+

So you can MASTER_LOG_POS set anything in the Pos column.

This should set up all the required configuration of the slave. You just need to tell slave to start the replication of the slave by the following command

mysql> start slave;

Mysql Replication: Configuring and Checking the Master

You can now check the MySQL’s data directory if the binary log is created. You should find a file something likemysql-bin.000001. You will also find a file mysql-bin.index.

In the mysql-bin.index file you will find the list of all the binary logs. i.e. If you restart the server a multiple times you will find your mysql-bin.index to have something like

./mysql-bin.000001
./mysql-bin.000002
./mysql-bin.000003

Now try this query in the mysql prompt show master logs; It should show something like this, depending the no. of binary logs present.

mysql> show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000004 | 9066115   |
| mysql-bin.000005 | 117       |
| mysql-bin.000006 | 98        |
+------------------+-----------+

Now it is essential to add a user account which the replication will use, Since we are granting a permission, you need to log in with a root access to mysql, and then run the following command.

mysql> GRANT REPLICATION SLAVE ON *.*
    -> TO 'repl'@'192.168%' IDENTIFIED BY 'slavepass';

Flush the privileges using mysql> flush privileges

This should ideally make your master ready for Replication.

Mysql Replication: Configuring Master

To configure the master we need to start binary logging, and some minor tweaking. Before we move ahead, I assume that you have the root or Administrator access to the sytem where you are setting up replication. First let us setup the Binary logging.

To enable binary logging, we need to configure the configuration file /etc/my.cnf on Linux. If you are in Windows, I guess it should be in c:\program files\MySQL\MySQL Server 5.0\my.ini. Open the file and add the following below the [mysqld] section

log-bin=mysql-bin
server-id=1

So the configuration should look something like

[mysqld]
log-bin=mysql-bin
server-id=1
... other parameters ...

[mysql]
... parameters for mysql client ...
  • The directive log-bin defines the file name of the binary log. All the binary logs that will be created in the data directory, are named something like mysql-bin.000004
  • The directive server-id is to give the servers in the replication a unique numeric ID. It has to be an integer. So we’ll call our Master Server as 1.

This would set the master, all we require is to restart the Server. You can restart the server (in linux) with either of the following commands

  • service mysql restart
  • /etc/rc.d/init.d/mysql restart
  • mysqladmin -u root -p shutdown
    and then mysqld_safe &

In windows, you’d be having an easier interface :D

Fronting Tomcat with Apache or IIS

Summary

Running cluster of Tomcat servers behind the Web server can be demanding
task if you wish to archive maximum performance and stability.
This article describes best practices how to accomplish that.

By Mladen Turk

Fronting Tomcat

One might ask a question Why to put the Web server in front of Tomcat
at all? Thanks to the latest advances in Java Virtual Machines (JVM)
technology and the Tomcat core itself, the Tomcat standalone is quite
comparable with performance to the native web servers.
Even when delivering static content it is only 10%
slower than recent Apache 2 web servers.

The answer is: scalability.

Tomcat can serve many concurrent users by assigning a separate thread of
execution to each concurrent client connection. It can do that nicely but
there is a problem when the number of those concurrent connections rise.
The time the Operating System will spend on managing those threads will degrade
the overall performance. JVM will spend more time managing and switching those
threads then doing a real job, serving the requests.

Besides the connectivity there is one more significant problem, and it caused
by the applications running on the Tomcat. A typical application will process
client data, access the database, do some calculations and present the data
back to the client. All that can be a time consuming job that in most cases
must be finished inside half a second, to achieve user perception of a working
application. Simple math will show that for a 10ms application response time you
will be able to serve at most 50 concurrent users, before your users start
complaining. So what to do if you need to support more users?
The simplest thing is to buy a faster hardware, add more CPU or add more boxes.
A two 2-way boxes are usually cheaper then a 4-way one, so adding more boxes
is generally a cheaper solution then buying a mainframe.

First thing to ease the load from the Tomcat is to use the Web server
for serving static content like images, etc..

Figure 1.
Figure 1. Generic configuration

Figure 1. shows the simplest possible configuration scenario. Here the
Web server is used to deliver static context while Tomcat only does the
real job – serving application. In most cases this is all that you will need.
With 4-way box and 10ms application time you’ll be capable of serving 200
concurrent users, thus giving 3.5 million hits per day, that is by all
means a respectable number.

For that kind of load you generally do not need the Web server in front of
Tomcat. But here comes the second reason why to put the Web server in front, and
that is creating an DMZ (demilitarized zone). Putting Web server on a
computer host inserted as a “neutral zone” between a company’s private network
and the internet or some other outside public network gives the applications
hosted on Tomcat capability to access company private data, while securing
the access to other private resources.

Figure 2.
Figure 2. Secure generic configuration

Beside having DMZ and secure access to a private network there can
be many other factors like the need for the custom authentication for example.

If you need to handle more load you will eventually have to add more Tomcat
application servers. The reason for that can be either caused by the fact
that your client load just can not be handled by a single box or that you
need some sort of failover in case one of the nodes breaks.

Figure 3.
Figure 3. Load balancing configuration

Configuration containing multiple Tomcat application servers needs a load balancer
between web server and Tomcat. For Apache 1.3, Apache 2.0 and IIS Web servers
you can use Jakarta Tomcat Connector (also known as JK), because it offers
both software load balancing and sticky sessions. For the upcoming Apache 2.1/2.2
use the advanced mod_proxy_balancer that is a new module designed and integrated
within the Apache httpd core.


Calculating Load

When determining the number of Tomcat servers that you will need to satisfy
the client load, the first and major task is determining the Average Application
Response Time (hereafter AART). As said before, to satisfy the user experience
the application has to respond within half of second. The content received by the client
browser usually triggers couple of physical requests to the Web server (e.g. images). The
web page usually consists of html and image data, so client issues a series
of requests, and the time that all this gets processed and delivered is
called AART. To get most out of Tomcat you should limit the number of concurrent
requests to 200 per CPU.

So we can come with the simple formula to calculate the maximum
number of concurrent connections a physical box can handle:

                              500
    Concurrent requests = ( ---------- max 200 ) * Number of CPU's
                            AART (ms)

The other thing that you must care is the Network throughput between the
Web server and Tomcat instances. This introduces a new variable called
Average Application Response Size (hereafter AARS), that is the number of
bytes of all context on a web page presented to the user. On a standard
100Mbps network card with 8 Bits per Byte, the maximum theoretical
throughput is 12.5 MBytes.

                               12500
    Concurrent requests = ---------------
                            AARS (KBytes)

For a 20KB AARS this will give a theoretical maximum of 625 concurrent
requests. You can add more cards or use faster 1Gbps hardware if need
to handle more load.

The formulas above will give you rudimentary estimation of the number of
Tomcat boxes and CPU’s that you will need to handle the desired
number of concurrent client requests.
If you have to deploy the configuration without
having actual hardware, the closest you can get is to measure the AART on
a test platform and then compare the hardware vendor Specmarks.


Fronting Tomcat with Apache

If you need to put the Apache in front of Tomcat use the Apache2 with
worker MPM. You can use Apache1.3 or Apache2 with prefork MPM for handling
simple configurations like shown on the Figure 1. If you need to front
several Tomcat boxes and implement load balancing use Apache2 and worker
MPM compiled in.

MPM or Multi-Processing Module is Apache2 core feature and it is responsible
for binding to network ports on the machine, accepting requests,
and dispatching children to handle the requests.
MPMs must be chosen during configuration, and compiled into the server.
Compilers are capable of optimizing a lot of functions if threads are used,
but only if they know that threads are being used. Because some MPMs use threads
on Unix and others don’t, Apache will always perform better if the MPM is
chosen at configuration time and built into Apache.

Worker MPM offers a higher scalability compared to a standard prefork
mechanism where each client connection creates a separate Apache process.
It combines the best from two worlds, having a set of child processes each
having a set of separate threads. There are sites that are running
10K+ concurrent connections using this technology.

Connecting to Tomcat

In a simplest scenario when you need to connect to single Tomcat instance
you can use mod_proxy that comes as a part of every Apache distribution.
However, using the mod_jk connector will provide approximately double the performance.
There are several reasons for that and the major is that mod_jk manages a
persistent connection pool to the Tomcat, thus avoiding opening and closing
connections to Tomcat for each request. The other reason is that mod_jk uses a custom
protocol named AJP an by that avoids assembling and disassembling header
parameters for each request that are already processed on the Web server.
You can find more details about AJP
protocol on the
Jakarta Tomcat connectors
site.

For those reasons you can use mod_proxy only for the low load sites
or for the testing purposes. From now on I’ll focus on mod_jk for fronting
Tomcat with Apache, because it offers better performance and scalability.

One of the major design parameters when fronting Tomcat with Apache
or any other Web server is to synchronize the maximum number of concurrent
connections. Developers often leave default configuration values from both Apache and
Tomcat, and are faced with spurious error messages in their
log files. The reason for that is very simple. Tomcat and Apache can each accept only
a predefined number of connections. If those
two configuration parameters differs, usually with Tomcat having
lower configured number of connections, you will be faced with the
sporadic connection errors. If the load gets even higher, your users will
start receiving HTTP 500 server errors even if your hardware is capable
of dealing with the load.

Determining the number of maximum of connections to the Tomcat
in case of Apache web server depends on the MPM used.

MPM configuration parameter
Prefork MaxClients
Worker MaxClients
WinNT ThreadsPerChild
Netware MaxThreads

On the Tomcat side the configuration parameter that limits the number
of allowed concurrent requests is maxProcessors with default value of
20. This number needs to be equal to the MPM configuration parameter.

Load balancing

Load balancing is one of the ways to increase the number of concurrent
client connections to the application server. There are two types of
load balancers that you can use. The first one is hardware load balancer
and the second one is software load balancer. If you are using load balancing
hardware, instead of a mod_jk or proxy, it must support a compatible passive
or active cookie persistence mechanism, and SSL persistence.

Mod_jk has an integrated virtual load balancer worker that can contain
any number of physical workers or particular physical nodes.
Each of the nodes can have its own balance factor or the worker’s
quota or lbfactor. Lbfactor is how much we expect this worker
to work
, or the workers’s work quota.
This parameter is usually dependent on the hardware topology itself, and
it offers to create a cluster with different hardware node configurations.
Each lbfactor is compared to all other lbfactors in the cluster and its
relationship gives the actual load. If the lbfactors are equal the workers
load will be equal as well (e.g. 1-1, 2-2, 50-50, etc…). If first
node has lbfactor 2 while second has lbfactor 1, than the first node
will receive two times more requests than second one.
This asymmetric load configuration enables to have nodes with different
hardware architecture.

In the simplest load balancer topology with only two nodes in the
cluster, the number of concurrent connections on a web server side
can be as twice as high then on a particular node. But …

    1 + 1 != 2

The upper statement means that the sum of allowed connections on a
particular nodes does not give the total number of connections allowed.
This means that each node has to allow a slightly higher number of
connections than the desired total sum. This number is usually a
20% higher and it means that

    1 * 1.2 + 1 * 1.2 == 2

So if you wish to have a 100 concurrent connections with two nodes,
each of the node will have to handle the maximum of 60 connections.
The 20% margin factor is experimental, and depends on the Apache
server used. For prefork MPMs it can rise up to 50%, while for
the NT or Netware its value is 0%. The reason for that is that
each particular child process menages its own balance statistics
thus giving this 20% error for multiple child process web servers.

    worker.node1.type=ajp13
    worker.node1.host=10.0.0.10
    worker.node1.lbfactor=1

    worker.node2.type=ajp13
    worker.node2.host=10.0.0.11
    worker.node2.lbfactor=2

    worker.node3.type=ajp13
    worker.node3.host=10.0.0.12
    worker.node3.lbfactor=1

    worker.list=lbworker
    worker.lbworker.type=lb
    worker.lbworker.balance_workers=node1,node2,node3

The minimum configuration for a three node cluster shown in the
upper example will give the 25%-50%-25% distribution of the load,
meaning that the node2 will get as much load as the rest of the two members.
It will also impose the following number of maxProcessors for each particular
node in case of the MaxClients=200.

    node1 :
        <Connector ... maxProcessors="60" ... />
    node2 :
        <Connector ... maxProcessors="120" ... />
    node3 :
        <Connector ... maxProcessors="60" ... />

Using simple math the load should be 50-100-50 but we needed to add the
20% load distribution error. In case this 20% additional load is not sufficient,
you will need to set the higher value up to the 50%. Of course the average
number of connections for each particular node will still follow the
load balancer distribution quota.

Sticky sessions and failower

One of the major problems with having multiple backend
application servers is determining the client-server relationship.
Once the client makes a request to a server application that
needs to track user actions over a designated time period,
some sort of state has to be enforced inside a stateless http
protocol. Tomcat issues a session identifier that
uniquely distinguishes each user. The problem with that session
identifier is that he does not carry any information about the
particular Tomcat instance that issued that identifier.

Tomcat in that case adds an extra jvmRoute configurable
mark to that session. The jvmRoute can be any name that will
uniquely identify the particular Tomcat instance in the cluster.
On the other side of the wire the mod_jk will use that jvmRoute
as the name of the worker in it’s load balancer list. This means
that the name of the worker and the jvmRoute must be equal.

jvmRoute is appended to the session identifier :

http://host/app;jsessionid=0123456789ABCDEF0123456789ABCDEF.jvmRouteName

When having multiple nodes in a cluster you can improve your application
availability by implementing failover. The failover means that if the
particular elected node can not fulfill the request the another node
will be selected automatically. In case of three nodes you are actually doubling your
application availability. The application response
time will be slower during failover, but none
of your users will be rejected. Inside the mod_jk configuration there
is a special configuration parameter called worker.retries that has default value of 3, but
that needs to be adjusted to the actual number of nodes in the cluster.

    ...
    worker.list=lbworker
    worker.lbworker.type=lb
    # Adjust to the number of workers
    worker.retries=4
    worker.lbworker.balance_workers=node1,node2,node3,node4

If you add more then three workers to the load balancer
adjust the retries parameter to reflect that number.
It will ensure that even in the worse case scenario the request
gets served if there is a single operable node. Of course, the
request will be rejected if there are no free connections available on the
Tomcat side , so you should increase the allowed number of connections
on each Tomcat instance. In the three node scenario (1-2-1)
if one of the nodes goes down, the other
two will have to take its load. So if the load is divided equally you will need
to set the following Tomcat configuration:

    node1 :
        <Connector ... maxProcessors="120" ... />
    node2 :
        <Connector ... maxProcessors="160" ... />
    node3 :
        <Connector ... maxProcessors="120" ... />

This configuration will ensure that 200 concurrent connections will
always be allowable no matter which of the nodes goes down. The reason for
doubling the number of processors on node1 and node3 is because they
need to handle the additional load in case node2 goes down (load 1-1).
Node2 also needs the adjustment because
if one of the other two nodes goes down, the load will be 1-2. As you
can see the 20% load error is always calculated in.

Figure 4.
Figure 4. Three node example load balancer
Figure 5.
Figure 5. Failover for node2

As shown in the two figures above setting maxProcessors depends both
on 20% load balancer error and expected single node failure. The
calculation must include the node with the highest lbfactor as
the worst case scenario.

Domain Clustering model

Since JK version 1.2.8 there is a new domain clustering model and
it offers horizontal scalability and performance of tomcat cluster.

Tomcat cluster does only allow session replication to all nodes in the cluster.
Once you work with more than 3-4 nodes there is too much overhead and risk in
replicating sessions to all nodes. We split all nodes into clustered groups.
The newly introduced worker attribute domain let
mod_jk know, to which other nodes a session gets replicated (all workers with
the same value in the domain attribute). So a load balancing worker knows, on
which nodes the session is alive. If a node fails or is being taken down
administratively, mod_jk chooses another node that has a replica of the session.

For example if you have a cluster with four nodes you can make
two virtual domains and replicate the sessions only inside the domains.
This will lower the replication network traffic by half

Figure 6.
Figure 6. Domain model clustering

For the above example the configuration would look like:

    worker.node1.type=ajp13
    worker.node1.host=10.0.0.10
    worker.node1.lbfactor=1
    worker.node1.domain=A

    worker.node2.type=ajp13
    worker.node2.host=10.0.0.11
    worker.node2.lbfactor=1
    worker.node2.domain=A

    worker.node3.type=ajp13
    worker.node3.host=10.0.0.12
    worker.node3.lbfactor=1
    worker.node3.domain=B

    worker.node4.type=ajp13
    worker.node4.host=10.0.0.13
    worker.node4.lbfactor=1
    worker.node4.domain=B

    worker.list=lbworker
    worker.lbworker.type=lb
    worker.lbworker.balance_workers=node1,node2,node3,node4

Now assume you have multiple Apaches and Tomcats. The Tomcats are clustered and
mod_jk uses sticky sessions. Now you are going to shut down (maintenance) one
tomcat. All Apache will start connections to all tomcats. You end up with all
tomcats getting connections from all apache processes, so the number of threads
needed inside the tomcats will explode.
If you group the tomcats to domain as explained above, the connections normally
will stay inside the domain and you will need much less threads.


Fronting Tomcat with IIS

Just like Apache Web server for Windows, Microsoft IIS maintains
a separate child process and thread pool for serving concurrent client
connections. For non server products like Windows 2000 Professional or
Windows XP the number of concurrent connections is limited to 10.
This mean that you can not use workstation products for production
servers unless the 10 connections limit will fulfil your needs.
The server range of products does not impose that 10 connection
limit, but just like Apache, the 2000 connections is a limit when
the thread context switching will take its share and slow down the
effective number of concurrent connections.
If you need higher load you will need to deploy additional web servers
and use Windows Network Load Balancer (WNLB) in front of Tomcat servers.

Figure 7.
Figure 7. WNLB High load configuration

For topologies using Windows Network Load Balancer the same rules are in place
as for the Apache with worker MPM. This means that each Tomcat instance
will have to handle 20% higher connection load per node than its real lbfactor.
The workers.properties configuration must be
identical on each node that constitutes WNLB, meaning that you will have to
configure all four Tomcat nodes.


Apache 2.2 and new mod_proxy

For the new Apache 2.1/2.2 mod_proxy has been rewriten and has
a new AJP capable protocol module (mod_proxy_ajp) and integrated
software load balancer (mod_proxy_balancer).

Because it can maintain a constant connection pool to backed
servers it can replace the mod_jk functionality.

    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
    LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
    ...
    <Proxy balancer://mycluster>
        BalancerMember ajp://10.0.0.10:8009 min=10 max=100 route=node1 loadfactor=1
        BalancerMember ajp://10.0.0.11:8009 min=20 max=200 route=node2 loadfactor=2
    </Proxy>
    ProxyPass /servlets-examples balancer://mycluster/servlets-examples

The above example shows how easy is to configure a Tomcat cluster with
proxy loadbalancer. One of the major advantages of using proxy is the
integrated caching, and no need to compile external module.

Mod_proxy_balancer has integrated manager for dynamic parameter changes.
It offers changing session routes or disabling a node for maintenance.

    <Location /balancer-manager>
        SetHandler balancer-manager
        Order deny,allow
        Allow from localhost
    </Location>
Figure 8.
Figure 8. Changing BalancerMember parameters

The future development of mod_proxy will include the option to
dynamically discover the particular node topology. It will also allow
to dynamically update loadfactors and session routes.


About the Author

Mladen Turk is a Developer and Consultant for JBoss Inc in Europe, where he is
responsible for native integration. He is a long time commiter for Jakarta Tomcat Connectors,
Apache Httpd and Apache Portable Runtime projects.


Links and Resources

Jakarta Tomcat connectors documentation

Apache 2.0 documentation

Apache 2.1 documentation

Powered by WordPress | Theme: by 85ideas. Editor by Khoanguyen