基础
JAVA 相关术语
Java SE API:Java基础类库开发接口
JRE:Java运行时环境,包含JVM+Jave核心类库
servlet
- 主要功能在于交互式地浏览和生成数据,生成动态Web内容。
- servlet是Server Applet的简称,翻译过来就是服务程序.好吧,这么说你可能还是不太懂,简单的讲,这个servlet是运行在服务器上的一个小程序,用来处理服务器请求的.进一步讲,我们知道,一般的网页程序,是由我们通过浏览器访问来实现的,在这个过程中,我们的浏览器发送访问请求,服务器接收请求,并对浏览器的请求作出相应的处理.这就是我们熟悉的B/S模型(浏览器-服务器模型).而servlet就是对请求作出处理的组件,运行于支持Java的应用服务器中.
Tomcat 概述
-
Tomcat不仅是一个应用程序服务器,也是一个web服务器
-
Tomcat实现类似Apache或Nginx的功能,可以理解为Tomcat实现了web功能,可以将编好的Java程序以web的方式让用户通过http协议来访问
- 运行流程:OS > JDK > Tomcat >APP
-
Tomcat 具有处理 HTML 页面的功能
-
它还是一个 servlet 和 JSP容器
-
官网:https://tomcat.apache.org/
-
官方文档:https://tomcat.apache.org/tomcat-8.5-doc/index.html
-
帮助文档:https://cwiki.apache.org/confluence/display/tomcat/
-
日志记录参考:https://tomcat.apache.org/tomcat-8.5-doc/logging.html
Tomcat 端口说明
8080/tcp # tomcat默认服务端口
8443/tcp # tomcat https服务端口
8005/tcp # tomcat管理端口(比如停止tomcat服务)
8009/tcp # tomcat AJP协议使用的端口Tomcat AJP 协议
-
Apache JServ Protocol 定向包协议
-
AJP协议是以二进制方式进行数据传输,而http是以文本方式进行传输的
-
在使用 Apache 作为反向代理转发到后端的 Tomcat 时,两者可以使用AJP协议进行数据传输(因为两者同属 Apache 基金会下的项目 所以都支持 AJP 协议?)
Tomcat 核心部分
- web容器:处理静态页面
- jsp容器:把jsp页面翻译成一般的servlet
- catalina:是一个servlet容器,用于处理servlet
Tomcat 基础使用
#查看当前变量设置和命令用法
[root@centos8 ~]# catalina.sh
#查看环境变量和版本信息
[root@centos8 ~]# catalina.sh version
#启动tomcat
[root@centos8 ~]# startup.sh
#关闭tomcat
[root@centos8 ~]# shutdown.sh
#指定10s后关闭tomcat,默认5s
[root@centos8 ~]# catalina.sh stop 10
#启动tomcat方法二
[root@centos8 ~]# catalina.sh start
#关闭tomcat方法二
[root@centos8 ~]# catalina.sh stopTomcat 目录结构说明
[root@centos8 ~]# cd /usr/local/tomcat/
[root@centos8 tomcat]# ls -d ./*/ | tr ' ' '\n'
./bin/ #服务启动、停止等相关程序和文件
./conf/ #配置目录
./lib/ #库目录
./logs/ #日志目录
./temp/ #临时目录
./webapps/ #应用程序、应用部署目录
./work/ #jsp编译后的结果文件,建议上线前提前预热访问针对特定APP目录设置专用配置文件
#准备测试用APP
[root@centos8 ~]# mkdir /usr/local/tomcat/webapps/azhengAPP
[root@centos8 ~]# echo 'azhengAPP_index.html' > /usr/local/tomcat/webapps/azhengAPP/index.html
[root@centos8 ~]# echo 'azhengAPP_index.htm' > /usr/local/tomcat/webapps/azhengAPP/index.htm
[root@centos8 ~]# echo 'azhengAPP_test.html' > /usr/local/tomcat/webapps/azhengAPP/test.html
#测试(没有模板时是根据全局配置文件来决定index文件的查找顺序)
[root@client ~]#curl 10.0.0.8:8080/azhengAPP/
azhengAPP_index.html
#拷贝模板到指定APP
[root@centos8 ~]# cp -a /usr/local/tomcat/webapps/ROOT/WEB-INF/ /usr/local/tomcat/webapps/azhengAPP/
#最终文件
[root@centos8 ~]# tree /usr/local/tomcat/webapps/azhengAPP/
/usr/local/tomcat/webapps/azhengAPP/
├── index.htm
├── index.html
├── test.html
└── WEB-INF
└── web.xml
#修改拷贝的模板
[root@centos8 ~]# vim /usr/local/tomcat/webapps/azhengAPP/WEB-INF/web.xml
...
<welcome-file-list>
<welcome-file>test.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
...
#测试访问
[root@client ~]#curl 10.0.0.8:8080/azhengAPP/
azhengAPP_test.htmlTomcat 处理请求的过程
假设来自客户的请求为:http://localhost:8080/test/index.jsp
- 浏览器端的请求被发送到服务器端的8080端口,Tomcat进程监听在此端口上。通过侦听的HTTP/1.1 Connector获得此请求
- Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待Engine的响应
- Engine 获得请求 localhost:8080/test/index.jsp,遍历它所有虚拟主机Host
- Engine 匹配到名为localhost的Host。(如果匹配不到,就把请求交给该Engine中的defaultHost来处理)
- localhost Host获得请求/test/index.jsp,匹配它所拥有的所有 Context
- Host 匹配到路径为 /test 的 Context
- path=/test的Context获得请求index.jsp,在它mapping table中对应的servlet
- Context 匹配到 URL PATTERN为*.jsp的servlet,对应于JspServlet类构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
- Host 把 HttpServletResponse 对象返回给 Engine
- Engine 把 HttpServletResponse 对象返回给 Connector
- Connector 把 HttpServletResponse 对象返回给浏览器端
Tomcat 反向代理
结合反向代理实现 Tomcat 部署
常见部署方式
standalone
- tomcat单独运行,用户直接访问tomcat,不推荐
单机实现反向代理
- 反向代理 单机运行,Nginx作为反向代理 将动态请求交给tomcat 静态请求nginx自身直接处理
- LNMT:Linux + Nginx + MySQL + Tomcat
反向代理多机(常用)
- 前置一台Nginx,给后端的多台tomcat做反向代理和负载均衡,将动态请求交给 tomcat 集群 静态请求nginx自身直接处理
- LN –> LMT:Linux + Nginx –> Linux + MySQL + Tomcat
反向代理多机多级
- LN –> LNMT:Linux + Nginx –> Linux + Nginx + MySQL + Tomcat
单机反向代理实现
动静资源都由nginx转发给tomcat:
环境说明:
一台主机,实现nginx和Tomcat
Tomcat上有两个主机:node1.azheng.com和node2.azheng.org
实现:
#修改本机的hosts文件,为nginx实现名称解析
[root@18 ~]# vim /etc/hosts
10.0.0.18 node1.azheng.com node2.azheng.org
#修改本机的默认nginx配置文件
[root@18 ~]# vim /etc/nginx/nginx.conf
location / {
proxy_pass http://node1.azheng.com:8080;
#proxy_pass http://node2.azheng.org:8080;
}
[root@18 ~]# systemctl enable --now nginx.service
#测试
[root@localhost ~]#curl node2.azheng.org
<h1>node1.azheng.com</h1>
[root@localhost ~]#curl node1.azheng.com
<h1>node1.azheng.com</h1>静态资源nginx处理,动态资源由nginx交给tomcat:
- jsp文件中实际上是由静态资源和动态组成,所有无法实现彻底的动静分离。实际上tomcat不太适合做动静分离,用它来管理程序的图片不好做动静分离部署
#修改本机的hosts文件,为nginx实现名称解析
[root@18 ~]# vim /etc/hosts
10.0.0.18 node1.azheng.com node2.azheng.org
#修改本机的默认nginx配置文件
[root@18 ~]# vim /etc/nginx/nginx.conf
location ~* \.jsp$ { #~*表示不区分大小写,将jsp后缀的请求全部转发给proxy_pass
proxy_pass http://127.0.0.1:8080;
}
#准备三个不同的资源文件
[root@18 ~]# echo /usr/local/tomcat/webapps/ROOT/test.html > /usr/local/tomcat/webapps/ROOT/test.html
[root@18 ~]# echo /usr/local/tomcat/webapps/ROOT/test.jsp > /usr/local/tomcat/webapps/ROOT/test.jsp
[root@18 ~]# echo /usr/share/nginx/html/test.html > /usr/share/nginx/html/test.html
#重启服务
[root@18 ~]# systemctl restart nginx.service tomcat.service
#测试
[root@localhost ~]#curl node1.azheng.com/test.html
/usr/share/nginx/html/test.html
[root@localhost ~]#curl node1.azheng.com/test.jsp
/usr/local/tomcat/webapps/ROOT/test.jsp
[root@localhost ~]#curl node2.azheng.org/test.html
/usr/share/nginx/html/test.html
[root@localhost ~]#curl node2.azheng.org/test.jsp
/usr/local/tomcat/webapps/ROOT/test.jspTomcat 负载均衡
- 实现tomcat集群负载均衡要考虑的问题:
- session保持:因为http是无状态协议,即每刷新一次页面或点击其他页面后切换到其他 tomcat server后这个server无法识别之前tomcat server发送给用户的sessionID 这样会导致用户的登录信息等内容丢失
- session持久化:
- session过期时间:
session保持方式
session 绑定
- nginx:source ip,cookie
- hapory:source ip,cookie
- 优点:配置简单
- 缺点:如果没有做session持久化,当目标服务器故障后,就会丢失此session,所以此方式生产中很少使用
session 复制集群
- Tomcat 提供了自己的多播集群,通过多播的方式将session同步到其他节点
- 缺点:
- tomcat的同步节点不宜过多,互相即时通讯同步信息会占用过多的带宽
- 每一台都拥有全部的session,内存损耗太多
session share server(常用)
- session 共享服务器,使用memcache、redis做共享的session服务器,此方式生产中常用
Nginx 实现 Tomcat 负载均衡
实验环境
| IP | 主机名 | 服务 | 软件 |
|---|---|---|---|
| 10.0.0.8 | proxy.azheng.vip | 调度器 | nginx |
| 10.0.0.18 | t1.azheng.vip | tomcat1 | JDK8、tomcat8 |
| 10.0.0.28 | t2.azheng.vip | tomcat2 | JDK8、tomcat8 |
tomcat准备
tomcat1
#准备虚拟主机
[root@tomcat-node1 ~]# vim /usr/local/tomcat/conf/server.xml
...
<Engine name="Catalina" defaultHost="t1.azheng.vip">
<Host name="t1.azheng.vip" appBase="/data/webapps/"
unpackWARs="false" autoDeploy="false">
</Host>
</Engine>
...
#准备页面文件
[root@tomcat-node1 ~]# mkdir -p /data/webapps/ROOT
[root@tomcat-node1 ~]# vim /data/webapps/ROOT/index.jsp
<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>
#修改安全权限
[root@tomcat-node1 ~]# chown -R tomcat.tomcat /data/
#重启tomcat服务
[root@tomcat-node1 ~]# systemctl restart tomcattomcat2
#准备虚拟主机
[root@tomcat-node2 ~]# vim /usr/local/tomcat/conf/server.xml
...
<Engine name="Catalina" defaultHost="t2.azheng.vip">
<Host name="t2.azheng.vip" appBase="/data/webapps/"
unpackWARs="false" autoDeploy="false">
</Host>
</Engine>
...
#准备页面文件
[root@tomcat-node2 ~]# mkdir -p /data/webapps/ROOT
[root@tomcat-node2 ~]# vim /data/webapps/ROOT/index.jsp
<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>
#修改安全权限
[root@tomcat-node2 ~]# chown -R tomcat.tomcat /data/
#重启tomcat服务
[root@tomcat-node2 ~]# systemctl restart tomcatnginx准备
#修改域名解析
[root@nginx ~]# vim /etc/hosts
10.0.0.8 proxy.azheng.vip
10.0.0.18 t1.azheng.vip
10.0.0.28 t2.azheng.vip
#安装nginx
[root@nginx ~]# yum -y install nginx
#修改nginx配置
[root@nginx ~]# vim /etc/nginx/nginx.conf
...
http {
upstream tomcat-server {
#ip_hash;
#hash $cookie_JSESSIONID
server t1.azheng.vip:8080;
server t2.azheng.vip:8080;
}
server {
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat-server;
}
}
}
...测试1
- 修改windows的host文件:10.0.0.8 proxy.azheng.vip
- 打开浏览器访问 proxy.azheng.vip 可以发现没刷新一次都是在 t1和t2间轮询,并且sessionID一直在变换
开启session粘性
基于源地址哈希实现
#修改nginx配置文件
[root@nginx ~]# vim /etc/nginx/nginx.conf
...
http {
upstream tomcat-server {
ip_hash; #启动源地址哈希
server t1.azheng.vip:8080;
server t2.azheng.vip:8080;
}
server {
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat-server;
}
}
}
#重新加载nginx配置文件
[root@nginx ~]# systemctl reload nginx.service
#测试访问:可以发现每刷新一次浏览器都是调度到同一个tomcat服务器上基于cookie的hash实现
#修改nginx配置文件
[root@nginx ~]# vim /etc/nginx/nginx.conf
...
http {
upstream tomcat-server {
hash $cookie_JSESSIONID; #启动基于cookie的hash
server t1.azheng.vip:8080;
server t2.azheng.vip:8080;
}
server {
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat-server;
}
}
}
#重新加载nginx配置文件
[root@nginx ~]# systemctl reload nginx.service
#测试访问:可以发现每刷新一次浏览器都是调度到同一个tomcat服务器上tomcat BUG
apache-tomcat-8.5.71.tar.gz二进制安装时执行 /apps/tomcat/bin/catalina.sh start 启动服务时无法开启8080端口,解决办法是修改vim /apps/tomcat/conf/server.xml ,将监听端口改成8090或其他,再重启服务使用修改的端口即可访问,访问成功后再将端口修改为8080停止服务重新启动即可
JMX 说明
- JMX在java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务,通常使用JMX来监控系统的运行状态
- 相关文档:https://www.jianshu.com/p/8c5133cab858