基础

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://cwiki.apache.org/confluence/display/TOMCAT/FAQ

  • 日志记录参考: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 stop

Tomcat 目录结构说明

[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.html

Tomcat 处理请求的过程

假设来自客户的请求为:http://localhost:8080/test/index.jsp

  1. 浏览器端的请求被发送到服务器端的8080端口,Tomcat进程监听在此端口上。通过侦听的HTTP/1.1 Connector获得此请求
  2. Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待Engine的响应
  3. Engine 获得请求 localhost:8080/test/index.jsp,遍历它所有虚拟主机Host
  4. Engine 匹配到名为localhost的Host。(如果匹配不到,就把请求交给该Engine中的defaultHost来处理)
  5. localhost Host获得请求/test/index.jsp,匹配它所拥有的所有 Context
  6. Host 匹配到路径为 /test 的 Context
  7. path=/test的Context获得请求index.jsp,在它mapping table中对应的servlet
  8. Context 匹配到 URL PATTERN为*.jsp的servlet,对应于JspServlet类构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
  9. Host 把 HttpServletResponse 对象返回给 Engine
  10. Engine 把 HttpServletResponse 对象返回给 Connector
  11. 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.jsp

Tomcat 负载均衡

  • 实现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 tomcat

tomcat2

#准备虚拟主机
[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 tomcat

nginx准备

#修改域名解析
[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