天天看點

Tomcat+apache實作負載均衡

實驗環境:Red Hat Enterprise Linux Server release 6.4

核心:Linux 2.6.32-358.el6.x86_64

下載下傳jdk和tomcat

Jdk

老版本:jdk-6u31-linux-x64-rpm.bin

http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-javase6-419409.html#jdk-6u45-oth-JPR

新版本:jdk-7u9-linux-x64.rpm

http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html

Tomcat :apache-tomcat-7.0.42.tar.gz

http://tomcat.apache.org/download-70.cgi

一、配置安裝tomcat,主機ip:172.16.5.12 hostname www.a.com

1、裝jdk

jdk-7u9-linux-x64.rpm直接rpm -ivh就行了

我使用jdk-6u31-linux-x64-rpm.bin安裝

# chmod +x jdk-6u31-linux-x64-rpm.bin
# ./jdk-6u31-linux-x64-rpm.bin
Press Enter to continue.....
直接ctrl+c退出
# cd /usr/java
# ls
default jdk1.6.0_31 latest
#vim /etc/profile.d/java.sh
export JAVA_HOME=/usr/java/latest
export PATH=$JAVA_HOME/bin:$PATH
# . /etc/profile.d/java.sh
# java -version
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04) 運作環境版本
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode)      

HotSpot(TM) Java虛拟機的名稱

2、裝tomcat

# tar xf apache-tomcat-7.0.42.tar.gz -C /usr/local/
# cd /usr/local/
# ln -sv apache-tomcat-7.0.42 tomcat
# vim /etc/profile.d/tomcat.sh
export CATALINA_HOME=/usr/local/tomcat
export PATH=$CATALINA_HOME/bin:$PATH
# . /etc/profile.d/tomcat.sh
# cd /usr/local/tomcat/bin
# catalina.sh version
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME:  /usr/java/latest
Using CLASSPATH:  /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Server version: Apache Tomcat/7.0.42
Server built: Jul 2 2013 08:57:41
Server number: 7.0.42.0
OS Name:  Linux
OS Version:  2.6.32-358.el6.x86_64
Architecture: amd64
JVM Version: 1.6.0_31-b04
JVM Vendor:  Sun Microsystems Inc.
# catalina.sh start 啟動服務
# catalina.sh stop 停止服務      

http://172.16.5.12:8080/

Tomcat 預設監聽8080端口

Tomcat+apache實作負載均衡

對Apache tomcat進行管理,需要配置 /usr/local/tomcat/conf/tomcat-users.xml

圖中有說明如何配置 ,需要加入兩組使用者

Tomcat+apache實作負載均衡
Tomcat+apache實作負載均衡
# vim /usr/local/tomcat/conf/tomcat-users.xml
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="qiufengsong" password="songqiufeng" roles="manager-gui,
admin-gui"/>      
Tomcat+apache實作負載均衡

3、可以為tomcat寫一個服務腳本,讓他開機自動啟動

# vim /etc/rc.d/init.d/tomcat
#!/bin/sh
# Tomcat init script for Linux.
#
# chkconfig: 2345 96 14
# description: The Apache Tomcat servlet/JSP container.
JAVA_HOME=/usr/java/latest
CATALINA_HOME=/usr/local/tomcat
export JAVA_HOME CATALINA_HOME
exec $CATALINA_HOME/bin/catalina.sh $*
# chmod +x /etc/rc.d/init.d/tomcat
# chkconfig --add tomcat
# chkconfig tomcat on
# service tomcat stop      

二、自定義一個虛拟主機,提供java程式頁面

下載下傳論壇程式JspRun!_6.0.0_GBK.zip

http://down.chinaz.com/soft/24848.htm

# mkdir -pv /web/app1
# unzip JspRun!_6.0.0_GBK.zip
# cp -rp upload/* /web/app1/
# vim /usr/local/tomcat/conf/server.xml
<Engine name="Catalina" defaultHost="www.a.com">
<Host name="www.a.com" appBase="/web/app1" unpackWARs="true" autoDeploy="true">
   <Context path="" docBase="/web/app1"/>
  <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="a_access_log." suffix=".txt"
    pattern="%h %l %u %t "%r" %s %b" />
</Host>
# service tomcat stop
# service tomcat start      

浏覽器中輸入http://172.16.5.12/install.jsp

安裝即可

Tomcat+apache實作負載均衡

中間要為論壇建立一個資料庫管理者帳号,要在論壇綁定的資料庫中建立,當然,mysqld也是實作要裝好的。

Tomcat+apache實作負載均衡

三、以上面配置好的tomcat為基礎,配置Apache基于mod_jk代理連接配接tomcat

ajp(Apache jserv protocal)

Apache做代理時,mod_proxy子產品對http、https、ajp三種協定都支援,mod_jk則隻支援ajp協定。

對于http協定時,tomcat伺服器就是一個完全意義上的web伺服器。

對于ajp協定時,tomcat伺服器是伺服器端,代理是用戶端,需要連接配接器連接配接。需要加載mod_jk.so,需要安裝jk連接配接器來實作。

Apache做代理,支援ajp協定,可以禁用http協定,這樣就可以把tomcat完全隐藏在後端,使用者隻有通過Apache代理才能通路。

Nginx跟後端tomcat建立連接配接必須基于http協定。

以Apache伺服器做代理,進行配置

Ip:172.16.5.11

Hostname www.b.com

後端tomcat伺服器

Ip:172.16.5.12

Hostname www.a.com

Apache伺服器配置

# yum -y groupintall “ Development tools”“ Server Platform Development”
# yum -y install httpd
# yum -y install apr-devel apr-util-devel httpd-devel(包含apxs)      

安裝jk連接配接器http://tomcat.apache.org/download-connectors.cgi

JK 1.2.37 Source Release tar.gz

# tar xf tomcat-connectors-1.2.37-src.tar.gz
# cd /root/tomcat-connectors-1.2.37-src/native
# ./configure --with-apxs=/usr/sbin/apxs
# make && make install
# ls /usr/lib64/httpd/modules/
# cd /etc/httpd/conf.d/
# vim mod_jk.conf
注釋:# Load the mod_jk
LoadModule jk_module modules/mod_jk.so #加載剛編譯的子產品
JkWorkersFile /etc/httpd/conf.d/workers.properties # jk連接配接器的工作屬性
JkLogFile logs/mod_jk.log # 日志
JkLogLevel debug #日志級别
JkMount /* TomcatA #加載的執行個體 與Apache代理的tomcat配置檔案中的執行個體相對應
JkMount /status/ stat1 #start1 在workers.properties 中定義      

Workers.properties

# cd /etc/httpd/conf.d/
# vim workers.properties
worker.list=TomcatA,stat1
worker.TomcatA.port=8009  #ajp協定監聽的端口
worker.TomcatA.host=172.16.5.12 #tomcat伺服器 ip
worker.TomcatA.type=ajp13
worker.TomcatA.lbfactor=1
worker.stat1.type = status #狀态資訊
JkMount /status/ stat1----------》worker.stat1.type = status      

TomcatA的解釋

在tomcat伺服器上編輯配置檔案

<Engine name="Catalina" defaultHost="www.a.com" jvmRoute="TomcatA">      

這樣就實作了Apache基于ajp協定對tomcat代理了,重新開機httpd即可生效。

四、Apache基于mod_jk代理tomcat實作負載均衡

1、另開一台虛拟機,配置ip

# ifconfig eth0 172.16.5.13/16 up
# hostname www.c.com      

2、安裝配置tomcat

3、在代理伺服器上修改配置檔案/etc/httpd/conf.d/workers.properties

4、便捷配置

# vim /etc/httpd/conf.d/workers.properties
worker.list = lbcluster1,stat1
worker.TomcatA.type = ajp13
worker.TomcatA.host = 172.16.5.12
worker.TomcatA.port = 8009
worker.TomcatA.lbfactor = 5
worker.TomcatB.type = ajp13
worker.TomcatB.host = 172.16.5.13
worker.TomcatB.port = 8009
worker.TomcatB.lbfactor = 5
worker.lbcluster1.type = lb
worker.lbcluster1.sticky_session = 0
worker.lbcluster1.method = R
worker.lbcluster1.balance_workers = TomcatA, TomcatB
worker.stat1.type = status      

5、後端tomcat伺服器為

Ip:172.16.5.12 www.a.com

Ip:172.16.5.13 www.c.com

# Vim /usr/local/tomcat/conf/server.xml
<Engine name="Catalina" defaultHost="www.a.com" jvmRoute="TomcatA">
<Host name="www.a.com" appBase="/web/app2" unpackWARs="true"
autoDeploy="true">
<Context path="" docBase="/web/app2"/>
  <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="a_access_log." suffix=".txt"
    pattern="%h %l %u %t "%r" %s %b" />
</Host>
# mkdir /web/app2
# vim /web/app2/index.jsp
<%@ page language="java" %>
<html>
 <head><title>TomcatA</title></head>
 <body>
 <h1><font color="red">TomcatA </font></h1>
 <table align="centre" border="1">
  <tr>
  <td>Session ID</td>
 <% session.setAttribute("abc","abc"); %>
  <td><%= session.getId() %></td>
  </tr>
  <tr>
  <td>Created on</td>
  <td><%= session.getCreationTime() %></td>
  </tr>
 </table>
 </body>
</html>
# service tomcat stop
# service tomcat start      
# Vim /usr/local/tomcat/conf/server.xml
<Engine name="Catalina" defaultHost="www.c.com" jvmRoute="TomcatB">
<Host name="www.c.com" appBase="/web/app2" unpackWARs="true"
autoDeploy="true">
<Context path="" docBase="/web/app2"/>
  <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="a_access_log." suffix=".txt"
    pattern="%h %l %u %t "%r" %s %b" />
</Host>
# mkdir /web/app2
# vim /web/app2/index.jsp
<%@ page language="java" %>
<html>
 <head><title>TomcatB</title></head>
 <body>
 <h1><font color="blue">TomcatB </font></h1>
 <table align="centre" border="1">
  <tr>
  <td>Session ID</td>
 <% session.setAttribute("abc","abc"); %>
  <td><%= session.getId() %></td>
  </tr>
  <tr>
  <td>Created on</td>
  <td><%= session.getCreationTime() %></td>
  </tr>
 </table>
 </body>
</html>
# service tomcat stop
# service tomcat start      

6、# vim /etc/httpd/conf.d/mod_jk.conf

# Load the mod_jk
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf.d/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug
JkMount /* lbcluster1
JkMount /status/ stat1
# service httpd start      

可以看到負載均衡

Tomcat+apache實作負載均衡
Tomcat+apache實作負載均衡
Tomcat+apache實作負載均衡

五、Apache基于mod_proxy代理tomcat實作負載均衡

加載mod_proxy,将之前配置的mod_jk.conf删除

負載一台伺服器

# cd /etc/httpd/conf.d/
# vim mod_proxy.conf
ProxyVia on
ProxyRequests Off
proxypreservehost off
<Proxy *>
 order allow,deny
 allow from all
</Proxy>
proxypass / http://172.16.5.11:8080/
proxypassreverse / http://172.16.5.11:8080/
<Location / >
 order allow,deny
 allow from all
</Location>      

負載均衡

# cd /etc/httpd/conf.d/
# vim mod_proxy.conf
ProxyVia on
ProxyRequests Off
proxypreservehost off
<Proxy balancer://hotcluster>
BalancerMember http://172.16.5.11:8080/
BalancerMember http://172.16.5.12:8080/
ProxySet lbmethod=byrequests
</Proxy>
<Location /balancer>
 SetHandler balancer-manager
 proxypass !
 Order Deny,Allow
 Allow from all
</Location>
<Proxy *>
 order allow,deny
 allow from all
</Proxy>
proxypass / balancer://hotcluster/
proxypassreverse / balancer://hotcluster/
<Location / >
 order allow,deny
 allow from all
</Location>      

注意:require all granted和order allow,deny;allow from all的差別

前者是2.4以後的版本,後者是2.2之前的版本

Tomcat+apache實作負載均衡

使用虛拟主機實作反向代理時,可以這樣配置完成虛拟主機的狀态資訊輸出

ProxyRequests Off
<proxy balancer://lbcluster1>
 BalancerMember ajp://172.16.5.11:8009 loadfactor=1
 BalancerMember ajp://172.16.5.12:8009 loadfactor=1
 ProxySet lbmethod=bytraffic
</proxy>
<VirtualHost *:80>
 ServerName localhost
 ProxyVia On
 ProxyPass / balancer://lbcluster1/ stickysession=JSESSIONID|jsessionid nofailover=On
 ProxyPassReverse / balancer://lbcluster1/
 <Location /balancer-manager>
 SetHandler balancer-manager
 Proxypass !
 order allow,deny
 allow from all
 </Location>
 <Proxy *>
 order allow,deny
 allow from all
 </Proxy>
 <Location / >
 order allow,deny
 allow from all
 </Location>
</VirtualHost>      

六、tomcat基于session複制的叢集

代理伺服器配置mod_proxy的代理配置

# vim /etc/httpd/conf.d/mod_proxy.conf

ProxyVia on
ProxyRequests Off
proxypreservehost off
<Proxy balancer://hotcluster>
BalancerMember http://172.16.5.11:8080/ loadfator=1
BalancerMember http://172.16.5.12:8080/ loadfator=1 #權重
ProxySet  lbmethod=byrequests
</Proxy>
<Location /balancer>
  SetHandler balancer-manager
  proxypass !
  order allow,deny
  allow from all
</Location>
<Proxy *>
  order allow,deny
  allow from all
</Proxy>
proxypass / balancer://hotcluster/ stickysession=jsessionid
proxypassreverse / balancer://hotcluster/
<Location  / >
  order allow,deny
  allow from all
</Location>      

注意:代理服務可能出現的問題,使用80端口可以代理到後端,使用8080端口卻代理不到後端

ap_proxy_connect_backend disabling worker for (172.16.5.11)

Permission denied: proxy: HTTP: attempt to connect to 172.16.5.12:8080 (172.16.5.12) failed

錯誤原因:

代理伺服器的安全政策啟用了,關閉安全政策

解決方案:

# getenforce
Enforcing
# setenforce 0
# getenforce
Permissive      

再次通路,發現可以代理到後端伺服器。

後端tomcat的配置

節點1:

# vim /usr/local/tomcat/conf/server.xml

</Host>
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
               channelSendOptions="8">
     <Manager className="org.apache.catalina.ha.session.DeltaManager"
              expireSessionsOnShutdown="false"
              notifyListenersOnReplication="true"/>
     <Channel className="org.apache.catalina.tribes.group.GroupChannel">
     <Membership className="org.apache.catalina.tribes.membership.McastService"
                 address="228.150.10.1"
                 port="45564"
                 frequency="500"
                 dropTime="3000"/>
     <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
               address="172.16.5.12"
               port="4000"
               autoBind="100"
               selectorTimeout="5000"
             maxThreads="6"/>
     <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
     <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
     </Sender>
     <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
     <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
     </Channel>
     <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
     filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/>
     <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
     <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
     tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/"
     watchDir="/tmp/war-listen/"  watchEnabled="false"/>
     <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
     <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
     </Cluster>
   </Engine>      

注意其中的多點傳播位址和ip位址,多點傳播位址要盡可能的使用自己定義的,ip位址是節點自己的位址

節點2:

</Host>
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
               channelSendOptions="8">
     <Manager className="org.apache.catalina.ha.session.DeltaManager"
              expireSessionsOnShutdown="false"
              notifyListenersOnReplication="true"/>
     <Channel className="org.apache.catalina.tribes.group.GroupChannel">
     <Membership className="org.apache.catalina.tribes.membership.McastService"
                 address="228.150.10.1"
                 port="45564"
                 frequency="500"
                 dropTime="3000"/>
     <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
               address="172.16.5.11"
               port="4000"
               autoBind="100"
               selectorTimeout="5000"
             maxThreads="6"/>
     <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
     <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
     </Sender>
     <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
     <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
     </Channel>
     <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
     filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/>
     <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
     <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
     tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/"
     watchDir="/tmp/war-listen/"  watchEnabled="false"/>
     <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
     <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
     </Cluster>
   </Engine>      

兩個節點的多點傳播位址一定要相同,IP位址都是各自自己的ip

我們在/usr/local/tomcat/webapps/中另外為tomcat建立站點目錄

# vim /usr/local/tomcat/webapps/testapp
# cd /usr/local/tomcat/webapps/testapp
# mkdir WEB-INF/{lib,classes} -pv (标準化配置)
# vim index.jsp
<%@ page language="java" %>
<html>
 <head><title>TomcatA</title></head>
 <body>
 <h1><font color="blue">TomcatA </font></h1>
 <table align="centre" border="1">
  <tr>
  <td>Session ID</td>
 <% session.setAttribute("abc","abc"); %>
  <td><%= session.getId() %></td>
  </tr>
  <tr>
  <td>Created on</td>
  <td><%= session.getCreationTime() %></td>
  </tr>
 </table>
 </body>
</html>
# service tomcat stop
# service tomcat start
# cd /usr/local/tomcat/conf/
# cp web.xml ../webapps/testapp/WEB-INF/
# vim ../webapps/testapp/WEB-INF/web.xml
version="3.0">
  <distributable />      

兩個節點配置相似,參照配置即可。需要重點注意的是,如果是三台主機配置,代理伺服器有兩張網卡,一個公網ip,一個内網網關,内網節點配置的ip需要将網關指向代理伺服器的内網位址。如果是自己做實驗,沒有用到那麼多位址,也最起碼要保證内網位址有預設網關,否則tomcat伺服器起不來的。

實驗效果圖

Tomcat+apache實作負載均衡
Tomcat+apache實作負載均衡

七、以nginx做代理,實作動靜分離,動态的内容代理到tomcat,靜态的内容代理到另一台主機,隻需要在nginx上添加以下配置即可。

location ~ \.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js)$ {
    proxy_pass http://172.16.5.13;
   proxy_set_header X-Real-IP $remote_addr;
 }
 location ~ \.(jsp|do|css)$ {
   proxy_pass http://172.16.5.12;這裡要注意對方端口,tomcat預設是8080
   }      

八、tomcat原理

tomcat是一個java程式的web平台,然而它又不是一個完整意義上的java EE(java企業級運作平台),是以,現如今很多企業使用開源元件如structs、spring和hibernate來建構複雜的應用。

tomcat是一個java程式的web平台,然而,它的運作需要借助一些類庫和工具,當然還有另外的環境機制,這些都是由jdk來實作的。可以說tomcat隻是一個java應用程式的容器,而站在它背後的jdk才是運作整個程式的核心。

Tomcat Server處理一個http請求的過程

Tomcat+apache實作負載均衡

假設來自客戶的請求為:

http://localhost:8080/wsota/wsota_index.jsp

1) 請求被發送到本機端口8080,被在那裡偵聽的Coyote HTTP/1.1 Connector獲得

2) Connector把該請求交給它所在的Service的Engine來處理,并等待來自Engine的回應

3) Engine獲得請求localhost/wsota/wsota_index.jsp,比對它所擁有的所有虛拟主機Host

4) Engine比對到名為localhost的Host(即使比對不到也把請求交給該Host處理,因為該Host被定義為該Engine的預設主機)

5) localhost Host獲得請求/wsota/wsota_index.jsp,比對它所擁有的所有Context

6) Host比對到路徑為/wsota的Context(如果比對不到就把該請求交給路徑名為""的Context去處理)

7) path="/wsota"的Context獲得請求/wsota_index.jsp,在它的mapping table中尋找對應的servlet

8) Context比對到URL PATTERN為*.jsp的servlet,對應于JspServlet類

9) 構造HttpServletRequest對象和HttpServletResponse對象,作為參數調用JspServlet的doGet或 doPost方法

10)Context把執行完了之後的HttpServletResponse對象傳回給Host

11)Host把HttpServletResponse對象傳回給Engine

12)Engine把HttpServletResponse對象傳回給Connector

13)Connector把HttpServletResponse對象傳回給客戶browser