一、连接器介绍
1、Tomcat连接器架构
基于Apache做为Tomcat前端的架构来讲,Apache通过mod_jk、mod_jk2或mod_proxy模块与后端的Tomcat进行数据交换。而对Tomcat来说,每个Web容器实例都有一个Java语言开发的连接器模块组件,在Tomcat6中,这个连接器是org.apache.catalina.Connector类。这个类的构造器可以构造两种类别的连接器:HTTP/1.1负责响应基于HTTP/HTTPS协议的请求,AJP/1.3负责响应基于AJP的请求。但可以简单地通过在server.xml配置文件中实现连接器的创建,但创建时所使用的类根据系统支持APR(Apache Portable Runtime)而有所不同。APR是附加在提供了通用和标准API的操作系统之上一个通讯层的本地库的集合,它能够为使用了APR的应用程序在与Apache通信时提供较好伸缩能力时带去平衡效用。
注意: mod_jk2模块目前已经不再被支持了,mod_jk模块目前还apache被支持,但其项目活跃度已经大大降低。因此,目前更常用的方式是使用mod_proxy模块。
2、连接器协议
Tomcat的Web服务器连接器支持两种协议:AJP(Apache JServ Protocol)和HTTP,它们均定义了以二进制格式在Web服务器和Tomcat之间进行数据传输,并提供相应的控制命令。
AJP协议: AJP是面向数据包的基于TCP/IP的协议,它在Apache和Tomcat的实例之间提供了一个专用的通信信道。目前正在使用的AJP协议的版本是通过JK和JK2连接器提供支持的AJP13,它基于二进制的格式在Web服务器和Tomcat之间传输数据,而此前的版本AJP10和AJP11则使用文本格式传输数据。它主要有以下特征:
HTTP协议:使用HTTP或HTTPS协议在Web服务器和Tomcat之间建立通信,此时,Tomcat就是一个完全功能的HTTP服务器,它需要监听在某端口上以接收来自于前端服务器的请求。
3、Tomcat的3种HTTP连接器
基于Java的HTTP/1.1连接器Coyote:
Tomcat6默认使用的连接器;它是Tomcat作为standalone模式工作时所用到的连接器,可直接响应来自用户浏览器的关于JSP、servlet和HTML的请求;此连接器是一个Java类,定义在server.xml当中,默认使用8080端口;
Java开发的高性能 HTTP/1.1连接器NIO:
它支持非阻塞式IO和Comnet,在基于库向tomcat发起请求时,此连接器表现不俗;但其实现不太成熟,有严重bug存在;
C/C++开发的 HTTP/1.1连接器native APR:
在负载较大的场景中,此连接器可以提供非常好的性能;APR即Apache Portable Runtime,它是一个能让开发者采用与平台无关的风格的方式来开发C/C++代码本地库。它能够很好的跨Windows, Linux和Unix平台工作。
启用APR连接器的条件:
native APR从三个主要方面优化了系统性能并提升了系统的伸缩能力:
连接器知识扩展
http://sishuok.com/forum/blogPost/list/4082.html
http://blog.sina.com.cn/s/blog_64a52f2a0101g3sq.html
下面以Tomcat 8为例实现简单反向代理与负载均衡的实验
二、在后端两个节点上安装tomcat
1、使用管理机在两个节点上安装好tomcat,tc2先配置好留着在后面配置负载均衡中使用
[root@DQ ~]# vim instaltc.sh
[root@DQ ~]# ansible tomcat -m copy -a "src=/root/instaltc.sh dest=/root/"
[root@DQ ~]# ansible tomcat -m shell -a "sh /root/instaltc.sh"
说明:由于在oracle官网下载jdk前需要手动接受 Oracle Binary Code License Agreement for Java SE,使用wegt在脚本中下载JDK时不方便,因此下面的脚本基于/usr/lcoal/src已经下载好jdk的二进制包的前提,此外,由于此前我已经10.33.100.22这台主机上配置好tomcat,为了方便,输出JAVA_HOME和CATALINA_HOME到环境变量的脚本就从10.33.100.22上直接复制了
2、配置Tomcat1,添加一个新的Host,并修改引擎中的默认主机为新增的Host
[root@tc1 ~]# vim /usr/local/tomcat/conf/server.xml
此处省去N行默认配置
Host组件是位于Engine容器中用于接收请求并进行相应处理的主机或虚拟主机
Context在某些意义上类似于apache中的路径别名,一个Context定义用于标识tomcat实例中的一个Web应用程序
path:相对于web服务器根路径而言的URI;如果为空“”,则表示为此webapp的根路径;如果context定义在一个单独的xml文件中,此属性不需要定义;
docBase:存放相应web应用程序的位置;也可以使用相对路径,起始路径为此Context所属Host中appBase定义的路径;切记,docBase的路径名不能与相应的Host中appBase中定义的路径名有包含关系,比如,如果appBase为deploy,而docBase绝不能为deploy-bbs类的名字;
reloadable:是否允许重新加载此context相关的web应用程序的类;默认为false;
3、检查语法,创建Host中定义的web应用程序的路径,并在该路径下提供测试页面,而后启动tomcat
[root@tc1 ~]# catalina.sh configtest
[root@tc1 ~]# mkdir -p /web/webapps/
[root@tc1 ~]# vim /web/webapps/index.jsp
[root@tc1 ~]# catalina.sh start
测试页面/web/webapps/index.jsp
4、将Tomcat1上的配置文件复制到Tomcat2上,稍作修改提供不同的测试页面,方便后面测试负载均衡效果,后测试Tomcat2能否正常访问
[root@tc1 ~]# cd /usr/local/tomcat/conf/
[root@tc1 conf]# scp server.xml 10.33.100.13:/usr/local/tomcat/conf/
[root@tc1 conf]# cd /web/webapps/
[root@tc1 webapps]# scp index.jsp 10.33.100.13:/web/webapps/
5、访问测试页面以确保服务正常启动
三、在ansible管理机上编译安装Apache
1、确保已准备好编译环境并解决了依赖关系,这里我只给出了apr和httpd的安装过程;编辑脚本源码安装apr和apr-util,目前对应的最新的版本是1.5.2和1.5.4;apr-util是apr的工具库,其可以让程序员更好的使用apr的功能;获取源码包http://apr.apache.org/
[root@DQ ~]# vim instalapr.sh
[root@DQ ~]# sh !$
instaltc.sh
2、编辑脚本源码安装apache,安装完成后将apache目录/bin下二进制程序输出到环境变量;提供SysV服务脚本,赋予其执行权限;修改httpd的主配置文件,设置其Pid文件的路径 ;将其添加至服务列表,设置开机自动启动,上述具体步骤可参见http://blog.csdn.net/celeste7777/article/details/47018865,这里不再赘述
[root@DQ ~]# vim instalhttpd.sh
[root@DQ ~]# sh !$
instalhttpd.sh
四、配置apache通过mod_proxy模块与Tomcat连接;实现反向代理
1、检测当前的apache是否支持mod_proxy、mod_proxy_http、mod_proxy_ajp和proxy_balancer_module(实现Tomcat集群时用到)等模块:
如果不支持,在配置文件httpd.conf中取消相应的模块前的注释,即可加载:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
2、备份并编辑配置文件httpd.conf,加载shm模块,指定虚拟主机的配置文件
[root@DQ ~]# cd /etc/httpd/
[root@DQ httpd]# cp httpd.conf httpd.conf.bak
[root@DQ httpd]# vim httpd.conf
3、第一种方法,基于proxy_http,编辑虚拟主机配置文件,让apache跟Tomcat的HTTP连接器进行整合
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也必须以“/”结尾,反之亦然。
ProxyPassReverse:
用于让apache调整HTTP重定向响应报文中的Location、Content-Location及URI标签所对应的URL,在反向代理环境中必须使用此指令避免重定向报文绕过proxy服务器。
测试Apache是否可以代理tomcat
4、第二种方法,基于proxy-ajp,让apache跟Tomcat的AJP连接器进行整合
另外,mod_proxy模块在httpd 2.1的版本之后支持与后端服务器的连接池功能,连接在按需创建在可以保存至连接池中以备进一步使用。连接池大小或其它设定可以通过在ProxyPass中使用key=value的方式定义。常用的key如下所示:
五、配置apache通过mod_jk模块与Tomcat连接
mod_jk是ASF的一个项目,是一个工作于apache端基于AJP协议与Tomcat通信的连接器,它是apache的一个模块,是AJP协议的客户端(服务端是Tomcat的AJP连接器)。
1、编辑脚本安装tomcat-connectors
[root@DQ ~]# vim instalcn.sh
[root@DQ ~]# sh !$
instalcn.sh
2、为了便于管理与mod_jk模块相关的配置,编辑配置文件/etc/httpd/extra/httpd-jk.conf添加内容如下:
说明:
LoadModule:在apache中装载模块mod_jk连接器模块
JkWorkersFile:用于指定保存了worker相关工作属性定义的配置文件,
JkLogFile:用于指定mod_jk模块的日志文件,
JkLogLevel:可用于指定日志的级别(info, error, debug),此外还可以使用JkRequestLogFormat自定义日志信息格式。
JkMount( JkMount < URL to match> < Tomcat worker name>)指定则用于控制URL与Tomcat workers的对应关系。
3、编辑/etc/httpd/httpd.conf,禁用前面配置的httpd-proxy.conf,并添加下面一行启用httpd-jk.conf
对于apache代理来说,每一个后端的Tomcat实例中的engine都可以视作一个worker,而每一个worker的地址、连接器的端口等信息都需要在apache端指定;以便apache可以识别并使用这些worker。约定俗成,配置这些信息的文件通常为workers.properties,其具体路径则是使用前面介绍过的JkWorkersFile指定的,在apache启动时,mod_jk会扫描此文件获取每一个worker的配置信息。
workers.properties文件一般由以下两类指令组成:
分别遵循如下使用语法
worker.list = < a comma separated list of worker names >
worker. name> .<property> = <property value>
其中worker.list指令可以重复指定多次,而worker name则是Tomcat中engine组件jvmRoute参数的值。根据其工作机制的不同,worker有多种不同的类型,这是需要为每个worker定义的一项属性woker.< work name >.type。常见的类型如下:
worker其它常见的属性说明:
4、根据httpd-jk.conf的指定,使用/etc/httpd/extra/workers.properties来定义一个名为Tomcat1的worker,并为其指定几个属性
至此,一个基于mod_jk模块与后端名为TomcatA的worker通信的配置已经完成,重启httpd服务即可生效。
六、配置基于mod_jk的负载均衡
注意:为了避免用户直接访问后端Tomcat实例,影响负载均衡的效果,建议在Tomcat的各实例上禁用HTTP/1.1连接器。
1、为每一个Tomcat实例的引擎添加jvmRoute参数,并通过其为当前引擎设置全局惟一标识符;需要注意的是,每一个实例的jvmRoute的值均不能相同。
2、修改/etc/httpd/extra/httpd-jk.conf为如下内容:
3、修改/etc/httpd/extra/workers.properties为如下内容:
在负载均衡模式中,worker专用的属性:
balance_workers:
用于负载均衡模式中的各worker的名称列表,需要注意的是,出现在此处的worker名称一定不能在任何worker.list属性列表中定义过,并且worker.list属性中定义的worker名字必须包含负载均衡worker。具体示例请参见后文中的定义。
sticky_session:{0/1}
在将某请求调度至某worker后,源于此址的所有后续请求都将直接调度至此worker,实现将用户session与某worker绑定。默认为值为1,即启用此功能。如果后端的各worker之间支持session复制,则可以将此属性值设为0。
method:{R/T/B}
默认为R,即根据请求的个数进行调度;T表示根据已经发送给worker的实际流量大小进行调度;B表示根据实际负载情况进行调度。
4、访问10.33.100.55,不断刷新,验证负载均衡效果
七、基于mod_proxy实现负载均衡
1、编辑配置文件 /etc/httpd/httpd.conf,启用配置文件htpd-proxy.conf
2、配置 httpd-proxy.conf,而后重启httpd服务即可访问网页测试负载均衡效果
Proxypass !表示不做反向代理,状态输出信息由apache自身提供
用于负载均衡集群时,Proxy指定是以balancer://开头,其可以接受一些特殊的参数的说明:
lbmethod:
apache实现负载均衡的调度方法,默认是byrequests,即基于权重将统计请求个数进行调度,bytraffic则执行基于权重的流量计数调度,bybusyness通过考量每个后端服务器的当前负载进行调度。
maxattempts:
放弃请求之前实现故障转移的次数,默认为1,其最大值不应该大于总的节点数。
nofailover:
取值为On或Off,设置为On时表示后端服务器故障时,用户的session将损坏;因此,在后端服务器不支持session复制时可将其设置为On。
stickysession:
调度器的sticky session的名字,根据web程序语言的不同,其值为JSESSIONID或PHPSESSIONID。
上述指令除了能在banlancer://或ProxyPass中设定之外,也可使用ProxySet指令直接进行设置,如:
BalancerMember http://www1.example.com:8080 loadfactor=1
BalancerMember http://www2.example.com:8080 loadfactor=2
ProxySet lbmethod=bytraffic
3、查看状态信息的输出
八、实现Tomcat基于内存复制的集群
1、所有启用集群功能的web应用程序,其web.xml中都须添加< distributable />才能实现集群功能。如果某web应用程序没有自己的web.xml,也可以通过复制默认的web.xml至其WEB-INF目录中实现。
2、编辑/usr/local/tomcat/conf/server.xml ,添加集群以下内容
Tomcat2上的配置是修改了下图3处红色标记,依为Tomcat2、10.33.100.13、10.33.100.13
Cluster
专用于配置Tomcat集群的元素,可用于Engine和Host容器中。定义在Engine容器中,Engine中的所有Host均支持集群功能;如果定义在某Host中,则表示仅对此主机启用集群功能。在Cluster元素中,需要直接定义一个Manager元素,这个Manager元素有一个其值为org.apache.catalina.ha.session.DeltaManager或org.apache.catalina.ha.session.BackupManager的className属性。同时,Cluster中还需要分别定义一个Channel和ClusterListener元素。
Channel
用于Cluster中给集群中同一组中的节点定义通信“信道”。Channel中需要至少定义Membership、Receiver和Sender三个元素,此外还有一个可选元素Interceptor。
Membership
用于Channel中配置同一通信信道上节点集群组中的成员情况,即监控加入当前集群组中的节点并在各节点间传递心跳信息,而且可以在接收不到某成员的心跳信息时将其从集群节点中移除。Tomcat6中Membership的实现是org.apache.catalina.tribes.membership.McastService。
Receiver
用于Channel定义某节点如何从其它节点的Sender接收复制数据,Tomcat6中实现的接收方式有两种BioReceiver和NioReceiver。
Sender
用于Channel中配置“复制信息”的发送器,实现发送需要同步给其它节点的数据至集群中的其它节点。发送器不需要属性的定义,但可以在其内部定义一个Transport元素。
Transport
用于Sender内部,配置数据如何发送至集群中的其它节点。Tomcat6有两种Transport的实现:
Valve
类似于过滤器,它可以工作于Engine和Host/Context之间、Host和Context之间以及Context和Web应用程序的某资源之间。一个容器内可以建立多个Valve,而且Valve定义的次序也决定了它们生效的次序。Tomcat6中实现了多种不同的Valve:
<Context path="/probe" docBase="probe">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.0\.0\.1"/>
Context>
其中相关属性定义有:
3、检查语法
[root@tc1 ~]# catalina.sh configtest
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/local/java/jdk1.8.0_60
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version: Apache Tomcat/8.0.28
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built: Oct 7 2015 18:25:21 UTC
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server number: 8.0.28.0
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Name: Linux
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Version: 2.6.32-358.el6.x86_64
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Architecture: i386
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Java Home: /usr/local/java/jdk1.8.0_60/jre
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Version: 1.8.0_60-b27
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Vendor: Oracle Corporation
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_BASE: /usr/local/src/apache-tomcat-8.0.28
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_HOME: /usr/local/src/apache-tomcat-8.0.28
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.endorsed.dirs=/usr/local/tomcat/endorsed
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.base=/usr/local/tomcat
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dcatalina.home=/usr/local/tomcat
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
Oct 25, 2015 11:52:14 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/i386:/lib:/usr/lib
Oct 25, 2015 11:52:14 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Oct 25, 2015 11:52:14 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
Oct 25, 2015 11:52:14 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-nio-8009"]
Oct 25, 2015 11:52:14 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
Oct 25, 2015 11:52:14 AM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 487 ms
4、不知道为什么启动catalina总是会出现闪退,端口监听好像也有问题
5、尝试添加组播路由,结果还是一样,知道错误的大神麻烦指点一下
日志信息
[root@tc1 ~]# tail -20 /usr/local/tomcat/logs/catalina.2015-10-25.log
25-Oct-2015 11:12:36.863 SEVERE [main] org.apache.coyote.AbstractProtocol.destroy Failed to destroy end point associated with ProtocolHandler ["ajp-nio-8009"]
java.lang.NullPointerException
at org.apache.tomcat.util.net.NioEndpoint.releaseCaches(NioEndpoint.java:305)
at org.apache.tomcat.util.net.NioEndpoint.unbind(NioEndpoint.java:481)
at org.apache.tomcat.util.net.AbstractEndpoint.destroy(AbstractEndpoint.java:823)
at org.apache.coyote.AbstractProtocol.destroy(AbstractProtocol.java:532)
at org.apache.catalina.connector.Connector.destroyInternal(Connector.java:1023)
at org.apache.catalina.util.LifecycleBase.destroy(LifecycleBase.java:305)
at org.apache.catalina.core.StandardService.destroyInternal(StandardService.java:588)
at org.apache.catalina.util.LifecycleBase.destroy(LifecycleBase.java:305)
at org.apache.catalina.core.StandardServer.destroyInternal(StandardServer.java:859)
at org.apache.catalina.util.LifecycleBase.destroy(LifecycleBase.java:305)
at org.apache.catalina.startup.Catalina.start(Catalina.java:629)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:351)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:485)