Tomcat结合Nginx or Apache实现负载均衡

理论在前面以及扯了一大堆了,这里就直接上实际操作。测试架构,前端调度器可以使用apache,Nginx或者HAProxy,这个架构只结合前端调度器和Tomcat。当然,一般Tomcat是会使用Nginx与LB Proxy进行交互。也就是说Tomcat Server上面会安装Nginx,用Nginx可以在上面提供静态页面。如果是动态页面的话,就直接由Nginx交给Tomcat。

请求到达Server,由Nginx接收请求,然后由Nginx自带的调度机制,静态的图片和文本就自己处理,动态的JSP页面就调度给Tomcat。

当然,既然Tomcat是Apache的项目,自然和Apache的结合更好,相对于Nginx只能使用http和Tomcat通信。Apache可以使用多个模块和Tomcat建立通信,不过因为Nginx在建立会话上比较高效,所以Apache与Tomcat结合使用反而相对较少,下面是通信方式比较。

而且Apache和Nginx都可以实现负载均衡。

实际操作:

准备工作:

iptables放行

插入放行规则,后面80是端口,放行什么端口就将80改成什么端口。如果对iptables不是很熟悉,并且在操作过程中出现访问不了的问题,请关闭iptables。

LB
# iptables -I INPUT -d 192.168.100.20 -p tcp --dport 80 -j ACCEPT
# iptables -I OUTPUT -s 192.168.100.20 -p tcp --sport 80 -j ACCEPT
//有时候会放行8080,8009等端口,有的时候还会自己设置端口,请自行放行

Tomcat
# iptables -I INPUT -s 192.168.100.20 -p tcp --dport 80 -j ACCEPT
# iptables -I OUTPUT -d 192.168.100.20 -p tcp --sport 80 -j ACCEPT
//这个地方也就端口需要注意以下。

删除规则(实验结束之后如果不不需要上面设定的iptables规则了)
# iptables -L -nv --line-number   //查看编号
# iptables -D INPUT 1             //删除INPUT链路上面的1号规则
# iptables -D OUTPUT 1

Nginx+Tomcat

Nginx and Tomcat

先说Nginx和Tomcat结合,拓扑图是这样的。

Nginx的安装我就不说了,实在不会安装的可以翻翻我前面的Blog。

Tomcat安装,因为我是用Ansible直接进行部署的,过程什么的非常之简单:

[root@Ansible tomcat]# tree
.
├── configFile
│   ├── java.sh
│   ├── tomcat
│   └── tomcat.sh
├── jdk_tomcat_install.retry
├── jdk_tomcat_install.yaml
└── packages
    ├── apache-tomcat-7.0.55.tar.gz
    └── jdk-7u67-linux-x64.rpm

jdk_tomcat_install.yaml:

[root@Ansible tomcat]# cat configFile/java.sh
export JAVA_HOME=/usr/java/latest
export PATH=$PATH:$JAVA_HOME/bin

[root@Ansible tomcat]# cat configFile/tomcat.sh
export CATALINA_HOME=/usr/local/tomcat
export PATH=$PATH:$CATALINA_HOME/bin

[root@Ansible tomcat]# cat configFile/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
case $1 in
restart)
    $CATALINA_HOME/bin/catalina.sh stop
    sleep 2
    exec $CATALINA_HOME/bin/catalina.sh start
    ;;
*)                                         //除restart功能catalina.sh脚本不带之外,其他都带了,所以就只将restart独立了,
   exec $CATALINA_HOME/bin/catalina.sh $1  //当然status功能也不带,有兴趣的盆友可以自己写,很简单的
   ;;                                      //而且如果用corosync+pacemaker实现 HA 的话是必须要有status功能的
esac

安装过程太简单了,我就把我的放这里。要是安装都不会请左转百度。

修改配置文件,添加一个虚拟主机:

# mkdir -pv /web/webapps/testapp
# vim /web/webapps/testapp/index.jsp
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
  <head>
    <title>JSP test page.</title>
  </head>
  <body>
    <h1>
    <% out.println("mail :  [email protected]"); %>
    </h1>
  </body>
</html>
# service tomcat start

测试页:

$ sudo echo "192.168.100.22  itcys.top" >> /etc/hosts
 //Windows用户请找自己的坑....百度一下就好

访问不了的童鞋请查看自己的iptables设置是否对访问机器放行

修改Nginx:

# vim /etc/nginx/conf.d/default.conf
	server {
    listen       80;
    server_name  itcys.top;                       //虚拟主机名为itcys.top

    location / {
        root   /web/html;                         //网页地址可改可不改
        index  index.jsp index.html index.htm;    //加入一个index.jsp
    }

    location ~ \.(jsp|do)$ {                      //以jsp或do结尾
        proxy_pass   http://192.168.100.22:8080;  //都将其调度到..
        proxy_set_header Host $http_host;         //将头部也向后面转发,因为tomcat也是设定的虚拟主机	
    }
}

# cat /web/html/test.html    //建立好目录和测试页面 
<h1>Nginx static test page</h1>
# touch /web/html/index.jsp

# service nginx start

修改hosts文件,让itcys.top指向Nginx Proxy Server IP。

Nginx Proxy + Tomcat

当然我们现在准备实现的架构是这样的:

这样Nginx和Tomcat就结合起来了,当然如果是在同一台主机,可以将proxy_pass http://192.168.100.22:8080; 改成proxy_pass http://127.0.0.1:8080;当然前端调度器依然是Nginx或者HAProxy,同一主机上面放Nginx+Tomcat就是上面那个动静分离的图。当然这个时候Client和Server之间还有一层Proxy。

那么整个系统的大概架构就会是上面这个架构图展示的。

先将另一台Tomcat启动起来。基础配置

# scp [email protected]:/usr/local/tomcat/conf/server.xml .
# mkdir -pv /web/webapps/testapp
# vim /web/webapps/testapp/index.jsp
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
  <head>
    <title>JSP test page.</title>
  </head>
  <body>
    <h1>
    <% out.println("Tomcat 192.168.100.21"); %>   //自己进行修改
    </h1>
  </body>
</html>

将Nginx Proxy配置修改一下。下面是需要修改的地方。

upstream TomcatServers {     //定义最精简的upstream
    server 192.168.100.21:8080;    //不指定weight默认为1;
    server 192.168.100.22:8080;
}                          //不指定调度算法默认为round-robin
server {
    location ~ \.(jsp|do)$ {
        proxy_pass   http://TomcatServers; 
    }
}

效果(如果看不到效果请看下各节点time是否同步):

如果后端Tomcat上还结合了Nginx的话,那么只需要将到达前端Nginx Proxy调度到后端Nginx监听的端口上面,由Nginx再进行动静分离,但是如果后端Tomcat已经是独立的一组服务器,静态Web也是一组独立的服务器了的话,那么就需要在配置文件添加上下面的内容:

location ~ \.(html|css|js|peng|jpg|png|gif)$ {        //如果可以的话还可以细分图片和文本
      proxy_pass   http://WebUpstreamServers;       //都将其调度到前面定义好的Web Upstream Servers
      proxy_set_header Host $http_host;            
}

这样一来,架构就会是这样:

业务规模再扩大就可以加入varnish或者再建立一组图片服务器。

Tomcat和Nginx的结合到这也差不多了。现在说说Apache和Tomcat的结合,

Apache+Tomcat

依旧是先将结合方式,然后再讲如何将Apache作为LB Proxy。

安装Apache并关闭Nginx

# service nginx stop; chkconfig nginx off
# yum -y install httpd
or
--enable-proxy --enable-proxy-http --enable-proxy-ajp (在编译的时候加上这些调度模块选项)
--enable-proxy-balancer   这个是Tomcat集群需要用到的模块

Apache配置 # vim /etc/httpd/conf.d/tomcat.conf <VirtualHost :80> ServerName itcys.top DocumentRoot “/web/staticPages” //存放静态页面 ProxyVia Off //控制在http首部是否使用Via,用于多级代理中使用 ProxyPreserveHost On //开启Host首部转发,后端Tomcat上面部署有多个虚拟主机的时候用 ProxyPass ~ ..(html|css|js|peng|jpg|png|gif)$ ! //静态页面不代理的方法 ProxyPass / http://192.168.100.21:8080/ //将Client请求转发给Tomcat ProxyPassReverse / http://192.168.100.21:8080/ //防止Url重写之后Client无法访问内网地址 Order Allow,Deny Allow from all </Location> </VirtualHost>

对应Tomcat配置:

<Connector port="8080" protocol="HTTP/1.1">

建立好测试页面,看效果:

再进行AJP连接:

Apache配置:

<VirtualHost *:80>
    ServerName itcys.top
    DocumentRoot "/web/staticPages"
    ProxyVia Off
    ProxyPreserveHost On
    ProxyPass ~ .*\.(html|css|js|peng|jpg|png|gif)$ !
    ProxyPass / ajp://192.168.100.21:8009/           //就将这里的http修改成了ajp
    ProxyPassReverse / ajp://192.168.100.21:8009/    //当然端口也要修改一下
    <Location />
       Order Allow,Deny
       Allow from all 
    </Location>
</VirtualHost>

Tomcat配置:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

效果和之前是一样的,我就不上图了。

再来说说LB负载均衡

<Proxy balancer://tomcatCluster>   //定义一个集群,就好像Nginx定义一个upstream一样
    BalancerMember http://192.168.100.21:8080 loadfactor=1
    BalancerMember ajp://192.168.100.22:8009 loadfactor=1
</Proxy>                           //可以同时定义tomcat的连接方式为http或者ajp
 
<VirtualHost *:80>
    ServerName itcys.top
    DocumentRoot "/web/staticPages"
    ProxyVia Off
    ProxyPreserveHost On
    ProxyPass ~ .*\.(html|css|js|peng|jpg|png|gif)$ !
    ProxyPass / balancer://tomcatCluster/          //调用的时候协议和上面同步就好
    ProxyPassReverse / balancer://tomcatCluster/
    <Location />
       Order Allow,Deny
       Allow from all 
    </Location>
</VirtualHost>

当然这样坑定不行,会话肯定要绑定,不然也需要后端Tomcat Session自己能处理好,后面说Tomcat实现Session保持,现在还是在调度上面下功夫

Apache配置:

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
 
<Proxy balancer://tomcatCluster>
    BalancerMember http://192.168.100.21:8080 loadfactor=1 route=tomcat21
    BalancerMember ajp://192.168.100.22:8009 loadfactor=1 route=tomcat22
    ProxySet stickysession=ROUTEID
</Proxy>
 
<Location /tomcatClusterManager>    //定义一个监控页面
    SetHandler balancer-manager
</Location>

<VirtualHost *:80>
    ServerName itcys.top
    DocumentRoot "/web/staticPages"
    ProxyVia Off
    ProxyPreserveHost On
    proxyPass /tomcatClusterManager !
    ProxyPass ~ .*\.(html|css|js|peng|jpg|png|gif)$ !
    ProxyPass / balancer://tomcatCluster/
    ProxyPassReverse / balancer://tomcatCluster/
    <Location />
       Order Allow,Deny
       Allow from all 
    </Location>
</VirtualHost>

Tomcat配置:只需要在engine段加上jvmRoute=tomcat#(和上面对应)就行

//Tomcat 21配置
<Engine name="Catalina" defaultHost="localhost" jvmRoute=tomcat21>
______________________________________________________________________
Tomcat 22配置
<Engine name="Catalina" defaultHost="localhost" jvmRoute=tomcat22>

现在就可以查看效果了,为了让效果更明显,为Tomcat提供了2个测试页面。把它们放到对应的文件夹

                  //Tomcat21 Test Page

<%@ page language="java" %>
<html>
  <head><title>Tomcat21</title></head>
  <body>
    <h1><font color="red">Tomcat21</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>


              //Tomcat22 Test Page

<%@ page language="java" %>
<html>
  <head><title>Tomcat22</title></head>
  <body>
    <h1><font color="blue">Tomcat22</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>

效果:

访问会被限制在同一个Tomcat上面,并且Session会保持

管理页面:

管理选项:

Apache mod_jk Tomcat

mod_jk是Apache和Tomcat的一种通信模块。可以在调度Client请求到Tomcat的时候实现负载均衡

安装mod_jk

软件版本:tomcat-connectors-1.2.41-src.tar.gz 安装方式:源码编译安装

# wget http://mirrors.cnnic.cn/apache/tomcat/tomcat-connectors/jk/tomcat-connectors-1.2.41-src.tar.gz
# tar xf tomcat-connectors-1.2.41-src.tar.gz
# cd tomcat-connectors-1.2.41-src/native
# ./configure --with-apxs=/* 注1 */
# make && make install

修改配置文件

# mv tomcat.conf tomcat.conf.bak
# cat /etc/httpd/conf.d/mod_jk.conf
LoadModule  jk_module  modules/mod_jk.so   //装载上面编译的模块
JkWorkersFile  /etc/httpd/conf.d/workers.properties    //工作进程的配置
JkLogFile  logs/mod_jk.log   //日志,logs/对于路径/var/log/httpd/
JkLogLevel  notice           //日志级别在notice和其之上都记录
JkMount  /*  Tomcat21        //转发,所有内容都转发给Tomcat21(后面会定义)

# vim /etc/httpd/conf.d/workers.properties
worker.list=Tomcat21            //定义一个worker
worker.Tomcat21.port=8009
worker.Tomcat21.host=192.168.100.21
worker.Tomcat21.type=ajp13      //ajp13表示对方支持ajp协议且运行着
worker.Tomcat21.lbfactor=1      //权重

效果:

定义集群

# cat mod_jk.conf 
LoadModule  jk_module  modules/mod_jk.so
JkWorkersFile  /etc/httpd/conf.d/workers.properties
JkLogFile  logs/mod_jk.log
JkLogLevel  notice
JkMount  /* tomcatCluster     //只修改了这个地方,其他都在另一个配置文件定义

# cat workers.properties 
worker.list=tomcatCluster    //定义一个worker
worker.Tomcat21.port=8009
worker.Tomcat21.host=192.168.100.21
worker.Tomcat21.type=ajp13
worker.Tomcat21.lbfactor=1
worker.Tomcat22.port=8009
worker.Tomcat22.host=192.168.100.22
worker.Tomcat22.type=ajp13
worker.Tomcat22.lbfactor=1
worker.tomcatCluster.type = lb               //定义tomcatCluster worker type为lb
worker.tomcatCluster.sticky_session = 1                   //1为支持session保持
worker.tomcatCluster.balance_workers = Tomcat21, Tomcat22 //定义tomcatCluster包含的worker

因为是保持session,所以开启了一个无痕窗口才看出效果:

好了,到这里基本上差不多了,HAProxy和Nginx差不多,所以就没说。基本上差不多,后面还有一篇讲Session持久的。也可以算是Tomcat Session HA