tomcat与前端通信方式
由于connector是tomcat与外部通信的唯一关口,tomcat与其他服务通信方式,即是conector与其他服务通信的方式,connector能够定义的通信类型如下:
connector定义类型
按照协议分
- http
- 1.1常用
- 1.2
- ajp
- 专用前端是httpd做web服务器时
按照io模型分
- nio
- nio2
- apr/native
协议与io模型组合
- http1.1 + nio
- http1.1 + nio2
- http1.1 + apr/native
- ajp + nio
- ajp + nio2
- ajp + apr/native
- ...
connector定义示例
<connector port="8080" protocol="HTTP/1.1" />
http1.1会自动选择NIO nio2 apr其中一个
<connector port="8080" protocol="org.apache.coyote.http11NioProtocol"/>
<connector port="8080" protocol="org.apache.coyote.http11Nio2Protocol"/>
<connector port="8080" protocol="org.apache.coyote.http11AprProtocol"/>
<connector port="8009" protocol="AJP/1.3" />
<connector port="8009" protocol="org.apache.coyote.ajp.AjpNioProtocol"/>
<connector port="8009" protocol="org.apache.coyote.ajp.AjpNio2Protocol"/>
<connector port="8009" protocol="org.apache.coyote.ajp.AjpAprProtocol"/>
tomcat与nginx
由于ajp只适用于httpd,所有tomcat和nginx只能通过http协议进行通信;
tomcat与httpd
ajp和http都可以用于和httpd通信,httpd前端有2个模块与tomact的connector通信,mod_proxy支持http和ajp,mod_jk支持ajp,
tomcat与静态web服务组合架构
- web服务器如nginx/httpd,同时担任静态web服务,以及动态请求转发的反代服务;
- web服务器如nginx/httpd,只担任静态web服务器,由前端nginx/haproxy/lvs进行动静请求的分离反代;
示例
tomcat+nginx
192.168.80.100 nginx
192.168.80.101 tomcat1
192.168.80.102 tomcat2
1,安装2台tomcat
安装jdk
安装tomcat
2,安装一台nginx
采用nginx官方repo,安装最新稳定版1.18即可
3,nginx配置一台虚拟主机,配置转发
[root@host1 nginx]# cat conf.d/tomcat.conf
upstream tomcat {
server 192.168.80.101:8080;
#server 192.168.80.102:8080;
}
server {
listen 80;
server_name 192.168.80.100;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location ~* \.(jsp|jspx|do) {
proxy_pass http://tomcat;
}
}
# nginx -t; nginx -s reload
4、浏览器访问
访问192.168.80.100/时,会返回nginx静态首页,访问.jsp后缀时,就根据配置转发到后端tomcat,tomcat运行jsp并返回处理后数据,文字部分直接返回,正常显示,图片部分返回的是包含在首页的链接,浏览器接收到首页后,分析其中链接,然后再次发起请求向80.100,但80.100的静态部分寻找路径是本机/usr/share/nginx/html目录,自然找不到对应文件,所有图片、样式等静态资源全部缺失,最终界面如图:
5、解决方法:
配置nginx将全部请求,无论动静都转发给tomcat处理
nginx配置如下:
[root@host1 nginx]# cat conf.d/tomcat.conf
upstream tomcat {
server 192.168.80.101:8080;
#server 192.168.80.102:8080;
}
server {
listen 80;
server_name 192.168.80.100;
location / {
# root /usr/share/nginx/html;
# index index.html index.htm;
proxy_pass http://tomcat;
}
# location ~* \.(jsp|jspx|do) {
# proxy_pass http://tomcat;
# }
}
[root@host1 nginx]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@host1 nginx]# nginx -s reload
浏览器访问,全部页面都可以正常显示,因为nginx将全部请求都交由tomcat处理,不存在动静分离,也不存在文件找不到问题;
合理配置nginx,使得其能找到静态资源,
如将ROOT目录下所有文件,以及examples这个webapp的目录拷贝到nginx的网页根目录下,再次访问,根据nginx和tomcat两台主机相同的目录路径,可以找到对应的静态资源文件,即可正常展示页面
# 在tomcat上,将对应目录原样复制到nginx上,
[root@host2 webapps]# scp -r examples/ 192.168.80.100:/usr/share/nginx/html
[root@host2 webapps]# scp -r ./ROOT/* 192.168.80.100:/usr/share/nginx/html
# nginx目录
# examples是将整个目录放在html/目录下,而ROOT是将其下文件直接放在html/目录下
[root@host1 nginx]# ll /usr/share/nginx/html/
total 188
-rw-r--r-- 1 nginx nginx 494 Apr 21 23:07 50x.html
-rw-r----- 1 nginx nginx 27235 Sep 5 17:45 asf-logo-wide.svg
-rw-r----- 1 nginx nginx 713 Sep 5 17:45 bg-button.png
-rw-r----- 1 nginx nginx 1918 Sep 5 17:45 bg-middle.png
-rw-r----- 1 nginx nginx 1401 Sep 5 17:45 bg-nav.png
-rw-r----- 1 nginx nginx 3103 Sep 5 17:45 bg-upper.png
drwxr-x--- 6 nginx nginx 83 Sep 5 17:49 examples
-rw-r----- 1 nginx nginx 21630 Sep 5 17:45 favicon.ico
-rw-r--r-- 1 nginx nginx 612 Apr 21 23:07 index.html
-rw-r----- 1 nginx nginx 12219 Sep 5 17:45 index.jsp
-rw-r----- 1 nginx nginx 7136 Sep 5 17:45 RELEASE-NOTES.txt
-rw-r----- 1 nginx nginx 5581 Sep 5 17:45 tomcat.css
-rw-r----- 1 nginx nginx 2066 Sep 5 17:45 tomcat.gif
-rw-r----- 1 nginx nginx 5103 Sep 5 17:45 tomcat.png
-rw-r----- 1 nginx nginx 2376 Sep 5 17:45 tomcat-power.gif
-rw-r----- 1 nginx nginx 67795 Sep 5 17:45 tomcat.svg
drwxr-x--- 2 nginx nginx 21 Sep 5 17:45 WEB-INF
访问examples可以,首页可以,其他如docs的webapp,由于没有复制对应目录到nginx下,所以仍然无法显示
tomcat与httpd
mod_jk下载地址:
http://tomcat.apache.org/download-connectors.cgi
配置手册:
http://tomcat.apache.org/tomcat-8.5-doc/connectors.html
mod_jk转发tomcat
mod_jk模块,反代tomcat,一般只用ajp协议
- httpd编译mod_jk模块
- 配置httpd,引入mod_jk,指明后端节点信息配置文件,uri转发规则配置
- 创建后端节点信息配置文件,其中配置后端节点信息
- 重启httpd
- tomcat:设置jvmRoute参数在engine上
- 设置connector,开启ajp13,配置监听地址,默认监听的所有ipv6地址,ipv4地址去连接拒绝;
1、编译mod_jk模块
下载源码包,解压;进入解压后目,再进入native目录,内有configure脚本
tar -xf tomcat-connectors-1.2.48-src.tar.gz
cd tomcat-connectors-1.2.48-src/
cd native
下载编译工具,包含apxs工具的httpd-devel包
yum install -y gcc gcc++ httpd-devel
编译,变异后的模块在apache-2.0目录下
./configure --with-apxs=/usr/bin/apxs
make
复制到httpd的模块目录下
cp apache-2.0/mod_jk.so /etc/httpd/modules/
2,导入mod jk,配置转发规则等
LoadModule jk_module modules/mod_jk.so
# 导入jk模块
# Update this path to match your conf directory location (put workers.properties next to httpd.conf)
# 节点信息配置文件
JkWorkersFile /etc/httpd/conf.d/workers.properties
# Where to put jk shared memory
# Update this path to match your local state directory or logs directory
JkShmFile /var/log/httpd/mod_jk.shm
# Where to put jk logs
# Update this path to match your logs directory location (put mod_jk.log next to access_log)
JkLogFile /var/log/httpd/mod_jk.log
# Set the jk log level [debug/error/info]
JkLogLevel info
# Select the timestamp log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
# Send everything for context /examples to worker named worker1 (ajp13)
# uri的转发规则,status的是状态页,其余的全部转发给后端TomcatA节点
######### "JkMount /* TomcatA" will send all request to TomcatA ########
JkMount /status/* statA
JkMount /* TomcatA
3,配置workers.properties文件
[root@host1 httpd]# cat conf.d/workers.properties
worker.list=TomcatA,statA
worker.TomcatA.type=ajp13
worker.TomcatA.host=192.168.80.102
worker.TomcatA.port=8009
worker.TomcatA.lbfactor=1
worker.statA.type = status
# 配置2个节点,一个是status内置状态页,一个是实际tomcat节点80.102
# 重启httpd
4,tomcat设置类型为ajp13的connector,和engine的jvmRoute参数
# 设置ajp13的连接器,地址要改为本机地址,默认的监听所有ipv6地址不不接受ipv4连接;
<Connector protocol="AJP/1.3"
address="192.168.80.102"
port="8009"
redirectPort="8443" secretRequired="" />
# engine加上jvmRoute参数
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA">
# 重启tomcat
5,访问测试
100为httpd服务器,访问其上jsp文件,都可以转发给后端102的tomcat,从而正常访问;
访问状态页也正常;
遇到的错误:
根据上述配置完成后,一直无法正常访问,即httpd和tomcat反代一直失败,最终查看catalina引擎的日志,发现如下错误, The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or ""
[root@host3 tomcat]# tailf logs/catalina.2020-09-06.log
...
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:342)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:473)
Caused by: java.lang.IllegalArgumentException: The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "".
# 根据google搜索结果,在connector部分,加入一个secretRequired=""后重启,即可;
<Connector protocol="AJP/1.3"
address="192.168.80.102"
port="8009"
redirectPort="8443" secretRequired="" />
# 之后可以正常访问;
mod_jk负载均衡tomcat
1、修改jk的配置文件,将转发目标设置为一个负载均衡的worker,此处命名为lb,是一个逻辑的节点,其下包含2个真实节点;
[root@host1 httpd]# cat conf.d/mod_jk.conf
LoadModule jk_module modules/mod_jk.so
...
######### "JkMount /* TomcatA" will send all request to TomcatA ########
JkMount /status/* statA
JkMount /* lb
2、修改workers.properties文件,定义lb和其下2个真实节点
定义了内置的stata状态页,lb一个逻辑节点,其下包含的2个真实节点,2个真实节点的地址,协议,端口,权重都根据实际情况设置,这里设置为权重一比一,关闭会话粘性,所以访问效果会是轮询!
[root@host1 httpd]# cat conf.d/workers.properties
worker.list=lb,statA
worker.statA.type = status
worker.lb.type=lb
worker.lb.sticky_session=false
worker.lb.balance_workers=TomcatA,TomcatB
worker.TomcatA.type=ajp13
worker.TomcatA.host=192.168.80.102
worker.TomcatA.port=8009
worker.TomcatA.lbfactor=1
worker.TomcatB.type=ajp13
worker.TomcatB.host=192.168.80.101
worker.TomcatB.port=8009
worker.TomcatB.lbfactor=1
3,分别配置2个后端tomcat节点
以TomcatA为例
# 配置connector和jvmRoute参数
<Connector protocol="AJP/1.3"
address="192.168.80.102"
port="8009"
redirectPort="8443" secretRequired="" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA">
# 创建一个test的webapp,
[root@host3 tomcat]# mkdir -pv /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
mkdir: created directory ‘/usr/local/tomcat/webapps/test’
mkdir: created directory ‘/usr/local/tomcat/webapps/test/WEB-INF’
mkdir: created directory ‘/usr/local/tomcat/webapps/test/WEB-INF/classes’
mkdir: created directory ‘/usr/local/tomcat/webapps/test/WEB-INF/lib’
# 写入首页文件,
[root@host3 tomcat]# vim /usr/local/tomcat/webapps/test/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>
# 重启
[root@host3 tomcat]# catalina.sh stop;catalina.sh start
TomcatB,修改对应参数为TomcatB即可,其余步骤一致
# connector和jvmRoute参数
<Connector protocol="AJP/1.3"
address="192.168.80.101"
port="8009"
redirectPort="8443" secretRequired=""/>
<Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatB">
# 首页文件不同,改成TomcatB
[root@host2 tomcat]# cat /usr/local/tomcat/webapps/test/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>
# 重启tomcat
4、重启httpd,tomcat,然后用无痕模式访问测试,排除缓存干扰
轮询效果如下:
若将sticky_session设置为ture,则是会话绑定效果;
mod_proxy转发tomcat
mod_proxy做tomcat的反代,可以使用http和ajp两种协议;配置反代时,协议头选择ajp或http,以及对应的端口即可;
mod_proxy必须的几种模块如下:yum安装自带这些模块,编译安装时,需要enable这些模块;
[root@host1 httpd]# httpd -k restart
[root@host1 httpd]# httpd -M |grep proxy
proxy_module (shared)
proxy_ajp_module (shared)
proxy_balancer_module (shared)
proxy_http_module (shared)
1、配置httpd,配置反代,
[root@host1 httpd]# cat conf.d/ajp.conf
<Location /status>
SetHandler balancer-manager
ProxyPass !
Require ip 192.168.80.0
</Location>
ProxyVia off
ProxyRequests off
ProxyPreserveHost off
ProxyPassMatch "^/(.*)$" ajp://192.168.80.101:8009/$1
ProxyPassReverse "^/(.*)$" ajp://192.168.80.101:8009/$1
<Proxy *>
Require all granted
</Proxy>
2、浏览器访问,成功反代;
mod_proxy负载均衡tomcat
1、配置后端节点组,在反代时,引用后端节点组名称即可
[root@host1 httpd]# pwd
/etc/httpd
[root@host1 httpd]# cat conf.d/ajp.conf
<proxy balancer://tomcatlb>
BalancerMember ajp://192.168.80.101:8009
BalancerMember ajp://192.168.80.102:8009
</proxy>
ProxyVia off
ProxyRequests off
ProxyPreserveHost off
ProxyPassMatch "^/(.*\.jsp)$" balancer://tomcatlb/$1
ProxyPassReverse "^/(.*\.jsp)$" balancer://tomcatlb/$1
<Proxy *>
Require all granted
</Proxy>
2、浏览器访问
访问:http://192.168.80.100/test/index.jsp,结果仍为轮询调度;