Apache+Tomcat+mod_jk实现负载均衡

最近公司提出了负载均衡的新需求,以减轻网站的高峰期的服务器负担。趁空闲时间我就准备了一下这方面的知识,都说有备无患嘛。网上相关资料很多,但是太散。我希望可以通过这篇随笔,系统的总结。

一、Tomcat集群对负载均衡可以用以下几种实现方式:

  • proxy
  • proxy_blancer
  • mod_jk

二、proxy、proxy_blancer和mod_jk的优缺点比较:

proxy的缺点:当其中一台tomcat停止运行的时候,apache仍然会转发请求过去,导致502网关错误。但是只要服务器再启动就不存在这个问题。

mod_jk方式的优点:Apache 会自动检测到停止掉的tomcat,然后不再发请求过去。缺点:当停止掉的tomcat服务器再次启动的时候,Apache检测不到,仍然不会转发请求过去。

proxymod_jk的共同优点是:可以只将Apache置于公网,节省公网IP地址资源。可以通过设置来实现Apache专门负责处理静态网页,让Tomcat专门负责处理jsp和servlet等动态请求。共同缺点是:如果前置Apache代理服务器停止运行,所有集群服务将无法对外提供。

proxy和mod_jk对静态页面请求的处理,都可以通设置来选取一个尽可能优化的效果。

mod_proxy_balancer和mod_jk都需要修改tomcat的配置文件配合<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
这三种Tomcat集群方式对实现最佳负载均衡都有一定不足,mod_proxy_balancer和mod_jk相对好些,mod_jk的设置能力更强些。lbfactor参数来分配请求任务。
apache自带mod_proxy功能模块中目前只可以实现两种不同的负载均衡集群实现方式,第一种是分工合作的的形式,通过各台主机负责不同的任务而实现任务分工。第二种是不同的机器在担任同样的任务,某台机器出现故障主机可以自动检测到将不会影响到客户端,而第一种却不能实现但第一种实现方式的优点在于他是主服务器负担相应没第二种大因为台只是提供跳转指路功能,形象的说他不给你带路只是告诉你有条路可以到,但到了那是否可以看到你见的人他已经不会去管你了。相比之下第二种性能要比第一种会好很多;但他们都有个共同点都是一托N形式来完成任务的所以你的主机性能一定要好。

 

本文的例子是利用ApacheHttpServer2.2+tomcat6+mod_jk做负载均衡。

一、准备工作

1、下载相关软件包:

  ApacheHttpServer

  Tomcat6.rar(复制两份)

  mod_jk.so(可以看做ApacheHttpServer的一个插件吧,用来给tomcat分流)

2、下载解压后的文件,复制到C盘根目录,如图:

  说明:如果apache是安装版的安装到指定目录(我的例子安装在了C盘更目录),如果是解压版直接解压到指定目录就OK。

  注意:不论是安装板还是解压版,在安装或解压后,必须在cmd命令窗口下安装apache服务。

  在安装后的apache下的bin目录,在地址栏输入cmd进入命令窗口,输入httpd -k install 安装服务成功后,可根据以下命令进行操作:

/*
 * 安装apache服务
 * 默认服务名称为apache+版本号
 * 如果想自定义服务名称可在该命令后加" -n 自定义服务名 "
 */
httpd -k install 

/*
 *开启服务
 */
httpd -k start

/*
 *停止服务
 */
httpd -k stop

/*
 *重启服务
 */
httpd -k restart

/*
 *卸载服务
 */
httpd -k uninstall

/*
 *测试服务配置
 */
httpd -t

/*
 *服务版本详情
 */
httpd -v

/*
 *命令行选项卡
 */
httpd -h

 

安装apache服务成功后,开启服务,在浏览器输入locahost,看到如下页面说明安装成功:

 

 

接下来,解压Tomcat,复制一份,一个命名为tomcat1,另一个命名为tomcat2。

  

 

 

3、把tomcat2中一个conf文件夹里的server.xml,将所有的端口号(port)开头的80全部改为90,下面红色框圈出的三处都要改。

 

 

3、在tomcat1中大约102行处,添加jvmRoute="tomcat1",在tomcat2中server.xml大约102行处,添加jvmRoute="tomcat2",如下:

<!--tomcat1中添加-->
 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
<!--tomcat2中添加-->
 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">

 

4、在tomcat2中大约第107~108行插入以下代码。

   在tomcat1的server.xml里也插入以下代码,更改<Receiver>里的port为4000,以保证两个tomcat<Receiver>端口不重复。

<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" bind="127.0.0.1" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/>  
    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="127.0.0.1" port="4002" autoBind="100" selectorTimeout="5000" maxThreads="6"/>  
    <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> 
      <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" timeout="60000" keepAliveCount="120000"/> 
    </Sender>  
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>  
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>  
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/> 
  </Channel>  
  <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>  
  <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>  
  <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>  
  <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> 
</Cluster>

5、复制下载好的mod_jk.so连接模块到apache安装目录下的modules文件夹。

6、打开apache安装目录下的conf文件夹,新建mod_jk.conf文件,新建workers.properties文件

  • mod_jk.conf文件内容如下:
#加载mod_jk Module 
LoadModule jk_module modules/mod_jk.so

#指定 workers.properties文件路径 
JkWorkersFile conf/workers.properties

#指定那些请求交给tomcat处理,"controller"为在workers.propertise里指定的负载分配控制器 
JkMount /*.jsp controller

#指定jk的日志输出文件 
JkLogFile logs/mod_jk.log

#指定日志级别 
JkLogLevel info

#查询日志格式
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
 
#Jk请求日志格式
JkRequestLogFormat "%w %V %T"
  • workers.properties文件内容如下:
#server 列表
worker.list = controller,tomcat1,tomcat2  

#========tomcat1======== 
#ajp13 端口号,在tomcat下server.xml配置,默认8009 
worker.tomcat1.port=8009    
#tomcat的主机地址,如不为本机,请填写ip地址
worker.tomcat1.host=localhost
#指定协议类型为AJP13定向包协议    
worker.tomcat1.type=ajp13 
#server的权重,值越高,分得的请求越多 
worker.tomcat1.lbfactor = 1   

#========tomcat2======== 
#ajp13 端口号,在tomcat下server.xml配置,默认8009,这里我们刚才改的是9009
worker.tomcat2.port=9009   
#tomcat的主机地址,如不为本机,请填写ip地址 
worker.tomcat2.host=localhost
#指定协议类型为AJP13定向包协议   
worker.tomcat2.type=ajp13 
#server的权重,值越高,分得的请求越多 
worker.tomcat2.lbfactor = 1

#========controller,负载均衡控制器======== 
worker.controller.type=lb
#指定分担请求的tomcat  
worker.controller.balance_workers=tomcat1,tomcat2
#是否为设置为黏着session,设置为1或true使用粘着session,设置为0或false不使用粘着session
worker.controller.sticky_session=false
#是否强制黏着 
worker.controller.sticky_session_force=false

#需要注意的地方
#        sticky_session      sticky_session_force     含义 
#        true                false                    SESSION会复制,有粘性 
#        true                true                     SESSION不复制,有粘性 
#        false               false                    SESSION会复制,无粘性 
#        false               true                     SESSION会复制,无粘性  

7、打开apache目录下的conf文件夹下的httpd.conf配置文件,在最下方添加以下代码:

#加载mod_jk.conf文件
Include conf/mod_jk.conf

8、ok,配置完毕!可以进行测试了。以下是测试步骤:

  • 新建一个web项目命名为test
  • 在该项目下新建test.jsp和WEB-INFO文件夹
    • test.jsp内容:
    • <%@ page contentType="text/html; charset=UTF-8" %>
      <%@ page import="java.util.*" %>
      <html>
        <head>
          <title>Apache2+Tomcat+mod_jk测试负载均衡</title>
          <style>
              html { color:#000; background:#F5F5F5; font-family:"微软雅黑","Microsoft YaHei","黑体","SimHei","宋体"; font-size:12px;}
              form{ font-size: 14px;}
              body{margin-left:300px;margin-right:auto;margin-top: 30px;}
              h3{display: inline;}
              input{ border: 1px solid #D3D3D3;width: 200px;height: 30px;margin-bottom: 10px;margin-top: 10px; text-indent: 10px;font-family: "微软雅黑";font-size: 14px;}
              .btn{ border: 1px solid #D3D3D3;border-radius: 15px;outline: 0;background: #DB6D4C;font-family: "微软雅黑";font-weight: bold;font-size: 12px;color: white;width: 100px;height: 30px; margin: 0 auto;}
              .btn:hover{background: #3A3A3A;}
              .content{ border: 1px solid #D3D3D3; margin: auto 10px; margin-bottom: 10px; width:500px;height:auto;padding:10 10 10 10 ;}
              table{ border: 1px solid #D3D3D3;margin: auto 10px;}
              td div{margin:10 10 10 10 ; float: left;}
          </style>
      </head>
      
      <body>
        <form action="http://localhost/test/test.jsp" method="POST">
          <table cellpadding="0" cellspacing="0">
            <tr>
              <td>
                <div>
                      session名:<input type="text" size=20 name="dataName"placeholder="请输入session名"><br/>
                      session值:<input type="text" size=20 name="dataValue"placeholder="请输入session值"><br>
                      <input type="submit" value="点击提交" class="btn">
                </div>
              </td>
            </tr>
          </table>
        </form>
      
        <div class="content">
          <h3>请求地址:</h3>
          <% out.println(request.getLocalAddr() + " : " + request.getLocalPort()+"<br>");%>
      
          <%
            out.println("<br><h3>Session ID:</h3> " + session.getId()+"<br/>");
            // 如果有新的 Session 属性设置
            String dataName = request.getParameter("dataName");
            if (dataName != null && dataName.length() > 0) {
               String dataValue = request.getParameter("dataValue");
               session.setAttribute(dataName, dataValue);
            }
      
            out.println("<br/><h3>Session 列表:</h3><br>");
            System.out.println("=========================================");
            System.out.println("Tomcat print session Info:");
            
            out.println("------------------------------------------<br/>");
      
            Enumeration e = session.getAttributeNames();
            while (e.hasMoreElements()) {
               String name = (String)e.nextElement();
               String value = session.getAttribute(name).toString();
               out.println("SessionName:"+ name + " | SessionValue:" + value + "<br>");
                   System.out.println("SessionName:" + name + " | SessionValue:" + value);
             }
            out.println("------------------------------------------<br/>");
          %>
        </div>
      </body>
      </html>

       

  • 在WEB-INFO下新建web.xml文件复制test项目到tomcat1、tomcat2文件夹的wabapp下分别部署
    • web.xml内容:
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://java.sun.com/xml/ns/javaee" 
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
       <display-name>test</display-name>
       <distributable/>
      </web-app>
  • 依次启动apache、tomcat1、tomcat2;在浏览器输入http://localhost/test/test.jsp按页面填写数据、刷新浏览器、宕掉其中一个节点等测试,观察页面信息和tomcat后台打印信息进行测试!
  • 测试页面如下:

      

 

 好了,这篇随笔就介绍到这里,欢迎大家多多批评,有不足之处还望各位前辈斧正!

 

 

 

 

posted @ 2015-11-27 14:36  codingcloud  阅读(363)  评论(0编辑  收藏  举报