基于apache实现tomcat的负载均衡

  1. 云栖社区>
  2. 博客>
  3. 正文

基于apache实现tomcat的负载均衡

科技小先锋 2017-11-14 11:20:00 浏览1097
展开阅读全文

一、Tomcat的连接器

   Tomcat的连接器分为两类:HTTP连接器和Web连接器

   Tomcat的HTTP连接器有三种:

      1.基于java的HTTP/1.1连接器,这也是Tomcat默认使用的连接器,即Coyote,它是Tomcat作物standalone模式工作时所用到的连接器,可直接响应来自用户浏览器的关于JSP、servlet和HTML的请求,定义在server.xml当中,默认使用8080端口

      2.Java开发的高性能NIO HTTP/1.1连接器,他支持非阻塞IO和Comnet,在基于库想tomcat发起请求时,此连接器表现不俗,但其实先不成熟,有严重bug存在

      3.C/C++开发的native APR HTTP/1.1连接器:在负载较大场景中,此连接器可以提供非常好的性能,APR及Apache Portable Runtime,它是一个能够让开发者采用与平台无关的风格的方式来开发C/C++代码本地库,它能够很好的跨Windows,Linux和类Unix平台工作,此连接器从三个主要方面优化了系统性能并提升了系统的伸缩能力:(1)使用sendfike()内核模式调用发送大的静态文件;(2)仅使用一个native code保持大量的连接;(3)使用能够加速SSL请求处理的OpenSSL本地代码

   启用APR连接器的条件:

       1.将连接器的protocol属性设定为org.apache.coyote.http11.Http11AprProtocol

       2.APR的库文件已经在系统库五年级的搜索路径内

   基于连接器提供Tomcat性能的方法

       1.设置tcpNoDelay属性值为“true”

       2.通过maxKeepAliveRequest属性调整允许keep-alive功能的请求的最大数目,值为1时表示禁用

       3.调整socketBuffer属性的值可以改变套接字缓冲的大小

       4.将enableLookups设置为false以禁用DNS反解

       5.Tomcat是一个多线程的Servlet容器,使用线程池能对服务器性能带去很大的影响,这主要通过maxThreads、maxSpareThreads和minSpareThreads来定义

       6.通过JAVA_OPTS.如-Xms和-Xmx设定JVM相关的参数以定义其使用内存的能力

   AJP(Apache JServ Protocol)是面向数据包的基于TCP/IP的协议,它在Apache和Tomcat的实例之间提供一个专用的通信新信道。目前常用的AJP协议的版本是1.3,它主要有以下特征

       1.在快速网络有着较好的特性表现,支持数据压缩传输

       2.支持SSL,加密及客户端证书

       3.支持Tomcat实例集群

       4.支持在apache和tomcat之间的连接的重用

   Apache可以通过mod_jk和mod_proxy模块跟Tomcat整合,但mod_proxy只有在apache2.2系列的版本才直接提供,但它可以提供更丰富的功能和安全性,而对于apache1.3和2.0系类mod_jk才更适用

   会话管理:分为标准会话管理器和持久会话管理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
标准会话管理器(StandardManager)
<Manager className="org.apache.catclina.session.StandardManager" maxInactiveInterval="7200" />
默认保存于$CATALINA_HOME/work/Catalina/<hostname>/<webapp-name>/下的SESSIONS.ser文件中
maxActiveSessions:最多允许的活动会话数量,默认为-1,表示不限制
maxINactiveInterval:非活动的会话超时时长,默认为60s
pathname:会话文件的保存目录
持久会话管理器(PersistentManager)
将会话数据保持至持久存储中,并且能在服务器意外中止后重新启动时重新加载这些会话信息,持久会话管理器支持将会话保存在文件存储(FileStore)和JDBC存储(JDBCStore)中
保存至文件中的示例
<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
    <Store className="org.apache.catalina.session.FileStore" directory="/data/tomcat-sessions" />
</Manager>
每个用户的会话会被保持至director指定的目录中的文件中,文件名为<session id>.session,通过后台线程每个一段时间(checkInterval参数定义,默认为60秒)检查一次超时会话
保存至JDBCStore中的示例
<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
    <Store className="org.apache.catalina.session.JDBCStore" driveName="com.mysql.jdbc.Driver" connectionURL="jdbc.mysql://localhost:3306/mydb?user=user;password=passwd"/>
</Manager>

二、基于Apahce的mod_proxy与tomcat连接

1.环境规划

   Apache 192.168.1.201

   Tomcat 192.168.1.202 192.168.1.203

   2.编译安装Apache,本处将不再累赘关于apr和apr-util的安装,关于两者的安装请移步至本人相关博客http://wangfeng7399.blog.51cto.com/3518031/1379373

1
2
3
[root@node1 ~]# tar xf httpd-2.4.9.tar.bz2
[root@node1 ~]# cd httpd-2.4.9
[root@node1 httpd-2.4.9]# ./configure --prefix=/usr/local/apache --sysconfdir=/etc/httpd --enable-so --enable-ssl --enable-cgi --enable-rewrite --with-zlib --with-pcre --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --enable-mpms-shared=all --with-mpm=event --enable-proxy --enable-proxy-http --enable-proxy-ajp --enable-proxy-balancer  --enable-lbmethod-heartbeat --enable-heartbeat --enable-slotmem-shm  --enable-slotmem-plain --enable-watchdog

   为程序提供启动脚本,并使其能够自动启动,具体内容其参照上面连接

如果启动不了的话,将LoadModule slotmem_shm_module modules/mod_slotmem_shm.so的注释取消即可

3.配置apache通过mod_proxy模块与Tomcat连接

    要使用mod_proxy与Tomcat实例连接,需要apache已经装载mod_proxy、mod_proxy_http、mod_proxy_ajp和proxy_banlancer_module(实现Tomcat集群时使用)等模块    

1
2
3
4
5
6
7
[root@node1 ~]# httpd -D DUMP_MODULES |grep proxy
 proxy_module (shared)
 proxy_balancer_module (shared)
 proxy_ftp_module (shared)
 proxy_http_module (shared)
 proxy_ajp_module (shared)
 proxy_connect_module (shared)

   在apache的全局配置端或虚拟主机中添加如下内容,也可以使用单独的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ProxyVia Off
ProxyRequests Off
ProxyPreserveHost Off
  ProxyPass  /  ajp://192.168.1.202:8009/
  ProxyPassReverse  /  ajp://192.168.1.202:8009/
<Location  / >
  Require all granted
</Location>
或者让apache跟Tomcat的HTTP连接器进行结合
ProxyVia Off
ProxyRequests Off
ProxyPreserveHost Off
  ProxyPass  /  http://192.168.1.202:8080/
  ProxyPassReverse  /  http://192.168.1.202:8080/
<Location  / >
  Require all granted
</Location>

   关于上述apache指令的说明

   ProxyPreserveHost {On|Off}:如果启用此功能,代理会将用户请求的报文中的Host:行发送给后端服务器,而不再使用ProxyPass指定的服务器地址。如果想在反向代理中支持虚拟主机,则需要开启此项,否则就不需要打开此功能

   ProxyVia {On|Off|Full|Block}:用于控制在http首部是否使用Via:主要用于控制在多级代理中控制代理请求的流向。默认为off,即不启用此功能,On表示每个请求和响应报文均添加Via:,Full表示每个Via:行都会添加当前apache服务器的版本号信息,Block表示每个代理请求报文中的Via:都会被移除

   ProxyRequests{On|Off}:是否开启apache正向代理的功能,启用此选项为了代理http协议必须启用mod_proxy_http模块。同时,如果为apache设置了ProxyPass,则必须将ProxyRequests设置为Off

   ProxyPass [path] ![url [key=value key=value ....]]:将后端服务器某URL与当前服务器的某虚拟机路径关联起来作为提供服务的路径。path为当前服务器上的某虚拟路径,url为后端服务器上的某URL路径,使用此指令必须ProxyRequests设置为Off。需要注意的是,如果path以"/"结尾,则对应的url也必须以"/"结尾,反之亦然,另外,mod_proxy模块在httpd2.1的版本之后支持与后端服务器的连接池功能,连接在按需创建的连接池中以备进一步使用,连接池大小或其他设定可以在ProxyPass中使用key=value的方式定义。常用的key如下所示

       min:连接池的最小容量,此值与实际连接个数无关,仅表示连接池最小有初始化的空间大小

       max:连接池的最大容量,每个MPM都有自己独立的容量,都与MPM本身有关,如Prefork的总数为1,而其它的则取决于ThreadsPerChild指令的值

       loadfactor:用于负载均衡集群配置中,定于对用后端服务器的权重,取值范围为1-100

       retry:当apache将请求发送至后端服务器得到错误响应时等待多长时间以后再重试,单位为秒

  如果Proxy指定是以balancer://开头,即用于负载均衡集群时,其还可以接受一些特殊的参数

       ldmethod:apache实现负载均衡的调度方法,默认是byrequests,即基于权重将统计请求个数进行调度,bytraffic则表示基于权重的流量技术来调度,bybusyness通过考量每个后端服务器的当前负载进行调度

       maxattempts:放弃请求之前实现故障转移的次数,默认为1,其最大值不应该大于总的节点数

       nofailover:取值为On或Off,设置为On时表示后端服务器故障时,用户session将损坏,因此,在后端服务器不支持session复制时可以将其设置为On

       stickysession:调度器的stick session的名称,根据web程序语言的不同,其值为JSESSIONID或PHPSESSIONID        wKiom1NuQPCCeXWbAARfpoq64Hg201.jpg

   可以看到我们的反向代理是成功的

4.配置mod_proxy实现负载均衡,将前面的配置文件改为如下内容,本处使用的为http协议

1
2
3
4
5
6
7
8
9
10
ProxyVia Off
ProxyRequests Off
ProxyPreserveHost Off
<Proxy balancer://wangfeng7399>
BalancerMember http://192.168.1.202:8080/ loadfactor=10 route=TomcatA
BalancerMember http://192.168.1.203:8080/ loadfactor=10 route=TomcatB
ProxySet lbmethod=bytraffic
</Proxy>
ProxyPass / balancer://wangfeng7399/
ProxyPassReverse / balancer://wangfeng7399/

   在192.168.1.202上添加testjsp页面,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ 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>

   在192.168.1.203上,添加test.jsp,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ 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>

wKiom1NuR1SjAkXBAACwOX5qtqo881.jpg

wKioL1NuRynBkCpEAADHBjqCSts293.jpg

   可以看到我们请求到的页面完全不同,这说明我们的负载均衡搭建成功,如果想要基于session绑定服务器应该添加stickysession=JSESSIONID

5.配置apache通过mod_jk模块与Tomcat连接

   mod_jk是ASF的一个项目,是一个工作与apache端基于AJP协议与Tomcat通信的连接器,它是apache的一个模块,是AJP的客户端(服务端是Tomcat的AJP连接器)

1
2
3
4
5
[root@node1 ~]# wget http://mirrors.cnnic.cn/apache/tomcat/tomcat-connectors/jk/tomcat-connectors-1.2.40-src.tar.gz
[root@node1 ~]# tar xf tomcat-connectors-1.2.40-src.tar.gz
[root@node1 ~]# cd tomcat-connectors-1.2.40-src/native/
[root@node1 native]# ./configure --with-apxs=/usr/local/httpd/bin/apxs
[root@node1 native]# make && make install

   apache要使用mod_jk连接器,需要在启动时加载此连接模块,为了便于管理与mod_jk模块的相关配置,这里使用一个专门的配置文件/etc/httpd24/extra/mod_jk.conf来保存相关的指令    

1
2
3
4
5
6
LoadModule  jk_module  modules/mod_jk.so
JkWorkersFile  /etc/httpd24/extra/workers.properties
JkLogFile  logs/mod_jk.log
JkLogLevel  error
JkMount  /*  TomcatA
JkMount  /status/  stat1

   除了需要使用LoadModule指令在apache中装载模块,mod_jk还需要在apache的主配置文件中设置其他一些指令来配置其工作属性。如JkWorkersFile则是用来指定保存了worker相关工作属性定义的配置文件,JklogFile则用于指定mod_jk模块的日志文件,JkLogLevel则可以用来指定日志的级别(info,error,debug),此外还可以使用JkRequestLogFormant定义日志信息的格式。而JkMount(格式:JkMount <本地URL> <Tomcat的工作目录>)来指定用于控制URL和Tomcat workers的对应关系

   为了让apache能使用自定义的配置文件,需要在httpd的主配置文件中,添加如下行

1
Include /etc/httpd24/extra/mod_jk.conf

   对于apache代理来说,每一个后端的Tomcat实例中的engine都可以视作为一个worker,而每一个worker的地址、连接器的端口等信息都需要在apache端指定以便apache可以识别并使用这些worker。约定俗成,配置这些信息的文件通常为workers.preoperties(即上面JkWorkersFile)指定的文件,在apache启动时,mod_jk会扫描此文件获取每一个worker的配置信息。

   workers.preoperties文件一般有两类指令组成,一是mod_jk可以连接的各worker名称列表,二是每一个worker的属性配置信息,它们分别遵循如下使用语法

   worker.list = <a comma separated list of worker names >

   worker.<worker name>.<property> = <property value>

   其中worker.list指令可以重复指定多次,而worker name则是Tomcat中engine组件jvmRoute参数的值

   根据某工作机制的不同,worker有多种不同的类型,这时需要为每个worker定义一项属性worker.<worker name>.type,常见的类型如下

       ajp13:此类型表示当前worker为一个运行着的Tomcat实例。

       lb:lb即load balancing,专用于负载均衡场景中的worker,此worker并不真正负责处理用户请求,而是将用户请求调度至其他类型为ajp13的worker

       status:用户显示分布式环境中各实际情况worker工作状态的特殊worker,他不处理任何请求,也不管连任何实际工作的worker实例

   worker其他常见的属性说明:

       host:Tomcat的worker实例所在的主机

       prot:Tomcat实例上AJP1.3连接器的端口

       connection_pool_minsize:最少要保持在连接池中的连接个数:默认为pool_size/2

       connection_pool_timeout:连接池中连接的超时时长

       mount:由当前worker提供的context路径,如果有多个则使用空格隔开,此属性可以有Jkmount指令代替

       retries:错误发生时的重试次数

       socket_timeout:mod_jk等待worker响应的市场,默认为0,即无限等待

       socket_keepalive:是否启用keep alive的功能,1表示启用,0表示禁用

       lbfactor:worker的权重,可以在负载均衡的应用场景中卫worker定义此属性

   另外,在负载均衡模式中,专用的属性还有

       balance_workers:用户负载均衡模式中的各worker的名称列表,需要注意的是,出现在此处的worker名称一定不能再任何worker.list属性列表中定义过,并且worker.list属性定义的worker名称必须包含负载均衡worker

       method:可以设定为R、T或B,默认为R,及根据请求的个数进行调度,T表示根据已经发送给worker的实际流量大小进行调度,B表示根据实际负载均衡情况进行调度

       sticky_session:在此将某请求调度至某worker后,源于此值得所有后续请求都将直接调度至此worker,实现将用户session与某worker绑定。默认的值为1,即启用此功能,如果后端的各worker之间支持session复制,则可以将此值设定为0

   根据前文中的指定,应该使用/etc/httpd24/extra/workers.properties来定义个名为TomcatA的worker,并为其指定几个属性,如下所示

1
2
3
4
5
6
worker.list=TomcatA,stat1
worker.TomcatA.port=8009
worker.TomcatA.host=192.168.1.202
worker.TomcatA.type=ajp13
worker.TomcatA.lbfactor=1
worker.stat1.type = status

   将192.168.1.202上tomcat的主配置文件中的内容作如下修改

1
2
3
4
将如下内容
<Engine name="Catalina" defaultHost="localhost">
修改为
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA">

wKioL1Nuau6DsbBJAAV-lB3al7I890.jpg

   可以看到现在访问正常了

   还可以查看后端的转状态显示页

wKioL1NucyGizOqTAARVYNIk6Zs756.jpg


6.配置基于mod_jk的负载均衡

     为了避免用户直接访问后端Tomcat实例,影响负载均衡的效果,建议在Tomcat的各实例上禁用HTTP/1.1连接器

     将/etc/httpd24/extra/mod_jk.conf中的内容修改为如下内容

1
2
3
4
5
6
LoadModule  jk_module  modules/mod_jk.so
JkWorkersFile  /etc/httpd24/extra/workers.properties
JkLogFile  logs/mod_jk.log
JkLogLevel  debug
JkMount  /*  lbcluster
JkMount  /status/  stat1

   将/etc/httpd24/extra/workers.properties内容修改为如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
worker.list=lbcluster,stat1
worker.TomcatA.port=8009
worker.TomcatA.host=192.168.1.202
worker.TomcatA.type=ajp13
worker.TomcatA.lbfactor=1
worker.TomcatB.port=8009
worker.TomcatB.host=192.168.1.203
worker.TomcatB.type=ajp13
worker.TomcatB.lbfactor=1
worker.lbcluster.type=lb
worker.lbcluster.sticky_session=0
worker.lbcluster.balance_workers=TomcatA,TomcatB
worker.stat1.type = status
~

   将192.168.1.202上tomcat的主配置文件中的内容作如下修改

1
2
3
4
将如下内容
<Engine name="Catalina" defaultHost="localhost">
修改为
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA">

   将192.168.1.203上的tomcat的主配置文件中的内容做如下修改

1
2
3
4
将如下内容
<Engine name="Catalina" defaultHost="localhost">
修改为
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatB">


wKioL1NudMrBuS_TAADFRcCUWh8594.jpg

wKiom1NudPXRMr_vAADO9ejDQBo215.jpg

   可以看到我们的集群搭建完成,如果想保持会话,只需要将worker.lbcluster.sticky_session=1即可

三、基于session复制保证会话的同步

    我们可以看到上面做的负载均衡不能保证会话的同步,下面我们来通过session复制的方式来保证会话的同步

   修改192.168.1.202和192.168.1.203上的Tomcat的主配置文件,在<engine>中添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<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.0.0.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="auto"
                      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=""/>
          <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>

   在需要做会话保持的虚拟主机上的WEB-INF的web.xml中添加<distributable/>,要不会话不会保持

   

wKioL1NuicrSYLalAADpe8T5oL8800.jpg

wKiom1NuifWSrwFvAAEINsSLPT0125.jpg

   终于写完了,好漫长的一次写作,希望对各位能够有所帮助,同时欢迎各位大神提意见



本文转自wangfeng7399 51CTO博客,原文链接:http://blog.51cto.com/wangfeng7399/1409341,如需转载请自行联系原作者


网友评论

登录后评论
0/500
评论
科技小先锋
+ 关注