tomcat

Tomcat和Java基础

软件架构模式

  • 分层架构:表现层,业务层,持久层,数据库层
  • 事件驱动架构:分布式一步架构,调度解耦
  • 微内核架构:即插件式架构,把大的架构做成小的核心模块和插件的形式。
  • 微服务架构:API REST-based,application REST-based,中心化消息,组成架构的每个模块都是一个服务,都需要监听套接字。
  • 基于空间的架构:云架构

Java代码的运行:
*.java(source code) –> javac –> *.class(bytecode)
jvm:class loader,加载程序的类文件,及程序的类文件依赖到的其它的类文件而后运行; 整个运行表现为一个jvm进程;threads;

java技术体系:

  • Java编程语言
  • Java Class文件格式
  • Java API
  • Java VM

    class loader
    执行引擎

JVM运行时区域:

  • 方法区:线程共享; 用于存储被JVM加载的class信息、常量、静态变量、方法等;堆:是jvm所管理的内存中占用空间最大的一部分;也是GC管理的主要区域;存储对象;
  • Java栈:线程私有,存储 线程自己的局部变量;
  • PC寄存器:线程私有的内存空间,程序的指令指针;
  • 本地方法栈:

安装JDK
了解当前的Java环境

~]#java -version

JDK分类:
openjdk
java-VERSION-openjdk:
The OpenJDK runtime environment.
java-VERSION-openjdk-headless:
The OpenJDK runtime environment without audio and video support.
java-VERSION-openjdk-devel:
The OpenJDK development tools.

注意:多版本并存时,可使用alternatives命令设定默认使用的版本。

Oracle JDK:
安装相应版本的rpm包;
jdk-VERSION-OS-ARCH.rpm
例如:jdk-1.8.0_25-linux-x64.rpm

注意:安装完成后,要配置JAVA_HOME环境变量,指向java的安装路径;
OpenJDK:
JAVA_HOME=/usr
Oracle JDK:
JAVA_HOME=/usr/java/jdk_VERSION

JVM是C语言编写的,其需要编写多个版本以适应不同的操作系统,而Java程序则只需一个版本就可以运行在不同的操作系统之上,即一次编写,处处运行。Java是纯对象编程。

编译:将源代码转换成对CPU指令集调用的格式,使其可以在CPU上运行。
Java的编译:将Java程序转换成可以在JVM上运行的格式。

编译运行和解释运行:
编译运行:将程序编译完成后在运行,运行速度快。
解释运行:编译一行,运行一行,运行速度慢。

J2SE:Standard Edition,标准版
J2ME:Mobile Edition,移动版
J2EE:Enterprise Edition,企业版

JDK:Java development killer Java开发工具箱

GC:垃圾回收器

Java程序运行过程:
.jsp -> Jasper -> .java -> JavaC -> .class -> Servlet ->

jsp时Java 2 EE类库中的组件。

使用Tomcat前的准备工作

安装JDK

jdk有两种,openjdk和oraclejdk,
#####rpm包方式安装官方OracleJDK(需先下载rpm包)

1
[root@node01 ~]# rpm -ivh jdk-8u191-linux-x64.rpm

安装目录为

1
2
3
4
5
[root@node01 ~]# ls /usr/java/ -l
total 0
lrwxrwxrwx 1 root root 16 Jan 16 14:59 default -> /usr/java/latest
drwxr-xr-x 8 root root 258 Jan 16 14:58 jdk1.8.0_191-amd64
lrwxrwxrwx 1 root root 28 Jan 16 14:59 latest -> /usr/java/jdk1.8.0_191-amd64

其中latest和default可以不是同一个,只需手动修改符号链接即可。

测试安装是否成功

1
2
3
4
5
6
[root@node01 bin]# pwd
/usr/java/jdk1.8.0_191-amd64/bin
[root@node01 bin]# java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

如果能看到Java的版本号说明JDK安装成功。

将Java加入环境变量

1
2
3
4
[root@node01 bin]# vi /etc/profile.d/java.sh
JAVA_HOME=/usr/java/default
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME PATH
安装openjdk
1
[root@node01 bin]# yum -y install java-11-openjdk-devel

jdk多版本是可以同时安装的,可以使用alternatives命令设定默认使用的版本。

安装Tomcat

yum安装

Tomcat相关包介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@node01 bin]# yum list tomcat*
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
Available Packages
tomcat.noarch
tomcat-admin-webapps.noarch #tomcat自身管理的包
tomcat-docs-webapp.noarch
tomcat-el-2.2-api.noarch
tomcat-javadoc.noarch
tomcat-jsp-2.2-api.noarch
tomcat-jsvc.noarch
tomcat-lib.noarch
tomcat-native.x86_64
tomcat-servlet-3.0-api.noarch
tomcat-webapps.noarch ##Tomcat示例
tomcatjss.noarch

安装Tomcat

1
[root@node01 bin]# yum -y install tomcat-admin-webapps tomcat-webapps tomcat-docs-webapp

安装完成后会自动生成一个unit file,可借助该unit file管理Tomcat。

Tomcat本身是java编写的文件,其各项功能的实现都需要通过类文件。提供类文件的包为tomcat-lib

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@node02 yum.repos.d]# yum info tomcat-lib
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
Available Packages
Name : tomcat-lib
Arch : noarch
Version : 7.0.76
Release : 8.el7_5
Size : 3.9 M
Repo : updates-local
Summary : Libraries needed to run the Tomcat Web container
URL : http://tomcat.apache.org/
License : ASL 2.0
Description : Libraries needed to run the Tomcat Web container.

tomcat默认监听的端口有

  • 8080:http
  • 8009:ajp
  • 8005: 管理接口
二进制安装

由于yum仓库提供的版本比较旧,因此可以选择到官网下载较新的安装包自行安装。

1
2
3
4
5
6
7
8
[root@node02 tools]# wget http://mirrors.hust.edu.cn/apache/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz
[root@node02 tools]# tar xf apache-tomcat-8.5.37.tar.gz -C /usr/local
[root@node02 local]# ln -s apache-tomcat-8.5.37 tomcat
[root@node02 tomcat]# useradd tomcat #添加一个普通通湖以运行tomcat
[root@node02 tomcat]# pwd
/usr/local/tomcat/
[root@node02 tomcat]# chown -R tomcat.tomcat . #更改属主和所属组
[root@node02 tomcat]# su - tomcat -c "/usr/local/tomcat/bin/catalina.sh start" #以tomcat的身份运行

经过以上步骤,实现了两种方式安装tomcat
OpenJDK 11 + Tomcat 7.0
Oracle JDK 8u191 + Tomcat 8.5

配置tomcat

tomcat的配置文件构成:

server.xml:主配置文件;
web.xml:每个webapp只有“部署”后才能被访问,它的部署方式通常由web.xml进行定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认部署相关的配置;
context.xml:每个webapp都可以专用的配置文件,它通常由专用的配置文件context.xml来定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认配置;
tomcat-users.xml:用户认证的账号和密码文件;
catalina.policy:当使用-security选项启动tomcat时,用于为tomcat设置安全策略; 
catalina.properties:Java属性的定义文件,用于设定类加载器路径,以及一些与JVM调优相关参数;
logging.properties:日志系统相关的配置;    log4j

Tomcat的核心组件:server.xml

<Server>
    <Service>
        <connector/>
        <connector/>
        ...
        <Engine>
            <Host>
                <Context/>
                <Context/>
                ...
            </Host>
            <Host>
                ...
            </Host>
            ...
        </Engine>
    </Service>
</Server>

每一个组件都由一个Java“类”实现,这些组件大体可分为以下几个类型:
顶级组件:Server
服务类组件:Service
连接器组件:http, https, ajp(apache jserv protocol)
容器类:Engine, Host, Context
被嵌套类:valve, logger, realm, loader, manager, …
集群类组件:listener, cluster, …

java时春面向对象的,所以其每个组建的应用都必须先定义成一个类。

  • 对象式编程:以数据为中心,代码服务于数据。

    类:类通过属性实例化,类要通过方法进行调用。

  • 过程式编程:以代码为中心,数据服务于代码。

JSP WebAPP的组织结构:

/: webapps的根目录
    index.jsp, index.html:主页;
    WEB-INF/:当前webapp的私有资源路径;通常用于存储当前webapp的web.xml和context.xml配置文件;
    META-INF/:类似于WEB-INF/;
    classes/:类文件,当前webapp所提供的类;
    lib/:类文件,当前webapp所提供的类,被打包为jar格式;


    root /web/htdocs
        bbs/index.html => /bbs/index.html
        images/logo.jpg => /images/logo.jpg
        WEB-INF/web.xml => /WIN-INF/web.xml 

webapp归档格式:

.war:webapp
.jar:EJB的类打包文件;
.rar:资源适配器类打包文件;
.ear:企业级webapp;

部署(deploy)webapp的相关操作:

deploy:将webapp的源文件放置于目标目录(网页程序文件存放目录),配置tomcat服务器能够基于web.xml和context.xml文件中定义的路径来访问此webapp;将其特有的类和依赖的类通过class loader装载至JVM;
    部署有两种方式:
        自动部署:auto deploy
        手动部署:
            冷部署:把webapp复制到指定的位置,而后才启动tomcat;
            热部署:在不停止tomcat的前提下进行部署;
                部署工具:manager、ant脚本、tcd(tomcat client deployer)等;                    
undeploy:反部署,停止webapp,并从tomcat实例上卸载webapp;
start:启动处于停止状态的webapp;
stop:停止webapp,不再向用户提供服务;其类依然在jvm上;
redeploy:重新部署;

部署一个新子站点示例

创建站点所需的目录及测试文件

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@node02 ~]# mkdir testapp/{WEB-INF,META-INF,classes,lib} -pv
[root@node02 testapp]# vi index.jsp
<%@ page language="java" %> #声明编程语言为java
<%@ page import="java.util.*" %> #导入java类库,名为java.util
<html>
<head>
<title>Test Page</title>
</head>
<body>
<% out.println("hello world"); #java代码,输出hello world
%>
</body>
</html>

修改配置文件
将创建的测试程序放入tomcat主站点内,如果是yum安装的tomcat,则主站点在/usr/share/tomcat/webapps/目录下,这里是二进制安装的tomcat,如下:

1
2
3
4
5
6
[root@node02 webapps]# cp -r /root/testapp-v0.1/ .
[root@node02 webapps]# ls
docs examples host-manager manager ROOT testapp-v0.1
[root@node02 webapps]# mv testapp-v0.1/ testapp
[root@node02 webapps]# pwd
/usr/local/tomcat/webapps

此时在浏览器访问该站点就可以看到该程序内容

将上述的子站点更改为一个独立的虚拟主机

修改配置文件,在engine内新增一个Host

1
2
3
4
[root@node01 ~]# vi /etc/tomcat/server.xml
<Host name="www.msq.com" appBase="/root/webapp"
unpackWARs="true" autoDeploy="true">
</Host>

tomcat的工作目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@node02 work]# pwd
/usr/local/tomcat/work
[root@node02 work]# tree
.
└── Catalina
└── localhost #当前虚拟主机的名称
├── docs
├── examples
├── host-manager
├── manager
├── ROOT
│   └── org
│   └── apache
│   └── jsp
│   ├── index_jsp.class
│   └── index_jsp.java
└── testapp
└── org
└── apache
└── jsp
├── index_jsp.class
└── index_jsp.java

14 directories, 4 files

tomcat的server.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@node02 tomcat]# vi conf/server.xml
<Server port="8005" shutdown="SHUTDOWN"> #server内建的管理接口,只要链上8005端口,送上一个'SHUTDOWN'字符串,就可以停止tomcat进程。所以比较危险,可以通过将该port改为-1来禁止监听该端口。
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Service name="Catalina"> #把connector与engine建立关联关系
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" /> #连接器,监听8080端口,使用http/1.1协议,连接超时时长为20s。
#redirectport以为如果用户发送的连接请求为https,则将会话重定向至8443端口。
#http协议的connector可以定义多个,但是要使用不同的端口。
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
#name为该引擎的名字,默认虚拟主机为localhost,JVMRoute为
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"> #虚拟主机,名字为localhost,
#网页文件根目录为webapp,可以使用相对路径或绝对路径,相对路径是相对tomcat的根木目录来说。
#是否自动展开WAR格式的文件,是否自动部署。
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" /> #定义日志格式
</Host>
</Engine>
</Service>
</Server>

定义虚拟主机host组件的常用属性说明
(1) appBase:此Host的webapps的默认存放目录,指存放非归档的web应用程序的目录或归档的WAR文件目录路径;可以使用基于$CATALINA_BASE变量所定义的路径的相对路径;
(2) autoDeploy:在Tomcat处于运行状态时,将某webapp放置于appBase所定义的目录中时,是否自动将其部署至tomcat;

在server.xml中新增一个host用于测试

1
2
3
<Host  name="www.msq.com"  appBase="/data/testapp"
unpackWARs="true" autoDeploy="true">
</Host>

此时在linux中按照配置生成测试文件如下

1
2
3
4
5
6
7
8
9
10
11
12
[root@node02 tomcat]# tree /data/
/data/
└── testapp
├── ROOT -> /data/testapp/webapp-v0.1/
└── webapp-v0.1
├── classes
├── index.jsp
├── lib
├── META-INF
└── WEB-INF

7 directories, 1 file

此时在浏览器中就可以基于主机名访问host,注意名字解析!

context组件

Context组件:类似于nginx中的alias。可内嵌与host组建中使用。

示例:
    <Context path="/PATH" docBase="/PATH/TO/SOMEDIR" reloadable=""/>

Context使用示例,在上述创建的host中内嵌一个Context

1
2
3
4
 <Host  name="www.msq.com"  appBase="/data/testapp"
unpackWARs="true" autoDeploy="true">
<Context path="/myapp" docBase="/myapps/testapp" reloadable=""/>
</Host>
valve组件

过滤器

Valve存在多种类型:

定义访问日志:org.apache.catalina.valves.AccessLogValve
定义访问控制:org.apache.catalina.valves.RemoteAddrValve 

 <Valve className="org.apache.catalina.valves.RemoteAddrValve" deny="172\.16\.100\.67"/>
manager app

管理应用程序的组件,程序的部署,反部署,启动,停止,增加虚拟主机或删除虚拟主机。
使用manager app
编辑tomcat-users.xml配置文件在其中增加一个管理用户

1
2
3
4
5
6
7
[root@apache ~]#cd /etc/tomcat/
[root@apache tomcat]#vi tomcat-users.xml
<tomcat-users>
...
<role rolename="manager-gui"/>
<user username="tomcat" password="centos" roles="manager-gui"/>
</tomcat-users>

之后在网站首页点击manager-app,输入用户名和密码即可使用manager app

在manager app中可以很方便的部署应用程序。

host manager

管理虚拟主机的组件,可以停止或者启动,删除虚拟主机。

使用nginx反向代理tomcat

使用nginx方向代理tomcat,如果被代理的tomcat上有多个虚拟主机时,要写被代理主机的主机名而非ip地址,如果内网主机太多时,就需要构建内网dns服务器。有两种代理方式
一、将所有的请求都代理给tomcat

location / {
    proxy_pass http://127.0.0.1:8080
}

二、动静分离的方式代理

location ~* \.(jsp|do) {
    proxy_pass http://127.0.0.1:8080;
}
location / {
    root /data/myapp/ROOT/;
}

将tomcat放在docker容器中,在宿主机上安装nginx,使用nginx方向代理tomcat

1
2
3
4
5
6
[root@apache ~]#docker run --name tc1 -v /uar/local/tomcat/webapps/ tomcat:8.5-alpine
[root@apache ~]# vi /etc/nginx/nginx.conf
server{
...
proxy_pass http:172.17.0.2:8080
}

使用httpd反向代理tomcat

httpd的代理模块:

proxy_module
proxy_http_module:适配http协议客户端;
proxy_ajp_module:适配ajp协议客户端;

proxy_http_module代理配置示例:
        <VirtualHost *:80>
            ServerName      tc1.magedu.com
            ProxyRequests Off
            ProxyVia        On
            ProxyPreserveHost On
            <Proxy *>
                Require all granted
            </Proxy>
            ProxyPass / http://tc1.magedu.com:8080/
            ProxyPassReverse / http://tc1.magedu.com:8080/ 
            <Location />
                Require all granted
            </Location>
        </VirtualHost>

            <LocationMatch "\.(jsp|do)$>
                ProxyPass / http://tc1.magedu.com:8080/
            </LocationMatch>

proxy_ajp_module代理配置示例:

<VirtualHost *:80>
    ServerName      tc1.magedu.com
    ProxyRequests Off
    ProxyVia        On
    ProxyPreserveHost On
    <Proxy *>
        Require all granted
    </Proxy>
        ProxyPass / ajp://tc1.magedu.com:8009/ 
        ProxyPassReverse / ajp://tc1.magedu.com:8009/ 
    <Location />
        Require all granted
    </Location>
</VirtualHost>

<%@ page language=”java” %>

TomcatA

TomcatA.magedu.com

<% session.setAttribute("magedu.com","magedu.com"); %>
Session ID<%= session.getId() %>
Created on <%= session.getCreationTime() %>

tomcat会话保持

session sticky:会话保持,需要调度器用调度算法解决,会影响调度器的调度效果,也会导致session单点故障风险。

session replication cluster:后端服务器,组织成集群,会增加服务器负载。

session server:在后端服务器之后设置session服务器专门存储session,session放在专门的session server上。将问题不断后置,对session server需要做高可用及主从。

在使用nginx负载均衡tomcat时,默认的算法为轮询(round roubin),这会导致每一个会话都会被当成一个新会话,没有会话保持的功能,session会话保持一:session sticky可以利用负载均衡器的算法实现:
配置负载均衡器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@docker ~]#vi /etc/nginx/nginx.conf
http{
...
upstream websrvs {
hash $remote_addr consistent;
server 192.168.34.108:8080;
server 192.168.34.103:8080;
}

server{
...
location / {
proxy_pass http://websrvs;
}
}
}

上述配置可以实现将每个请求根据源地址哈希之后,都转发给固定的web服务器响应,但是这会影响负载均衡的效果,也会导致session单点丢失的可能。

基于httpd的sessionsticky和blancer-manager功能
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
~]#vi /etc/httpd/conf.d/tomcat-httpd-claster.conf
<proxy balancer://tcsrvs>
BalancerMember ajp://172.18.100.67:8009
BalancerMember ajp://172.18.100.68:8009
ProxySet lbmethod=byrequests
</Proxy>
<VirtualHost *:80>
ServerName lb.magedu.com
ProxyVia On
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / balancer://tcsrvs/
ProxyPassReverse / balancer://tcsrvs/
<Location />
Require all granted
</Location>
<Location /balancer-manager>
SetHandler balancer-manager
ProxyPass !
Require all granted
</Location>
</VirtualHost>

session replication cluster

配置server.xml文件,在host配置段增加一个cluster的配置。

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
35
36
37
38
39
40
41
42
43
[root@apache tomcat]#vi /etc/tomcat/server.xml 
<Host ...>
<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>
</Host>

配置web.xml文件,添加以下元素

1
2
[root@apache tomcat]#vi /webapps/myapp/WEB-INF/web.xml 
<distributable/>

此时,session cluster就完成了配置,重启tomcat服务器就可以实现session replication cluster。

session server

将session会话保存到特殊的存储服务器上,存储系统性能要足够高,而且要有冗余能力,

基于memcached建立session server

memcached是一种基于cache的存储系统,

  • cache:无持久能力,重启后数据丢失。

redis是一种store类型的存储系统

  • store:持久是必备功能

memcached实用内存的方法,将分配的内存一次性占用完,做预分配,减少出现内存随便,
安装memcached

1
~]#yum -y install memcached

安装memcached客户端命令

1
~]#yum -y install libmemcached

tomcat要使用memcached作为session服务器,需要安装相关组件:
https://github.com/magro/memcached-session-manager

1
2
[root@apache tomcat]#wget http://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager/2.3.2/memcached-session-manager-2.3.2.jar
[root@apache tomcat]#wget http://repo1.maven.org/maven2/net/spy/spymemcached/2.12.3/spymemcached-2.12.3.jar

修改配置文件,对需要增加配置的虚拟主机增加一个context

1
2
3
4
5
6
7
8
9
~]#vi /etc/tomcat/server.xml
<Context path="/myapp" docBase="/webapps/myapp" reloadable="">
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:host1.yourdomain.com:11211,n2:host2.yourdomain.com:11211"
failoverNodes="n1"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
</Context>

此时重启tomcat局可以将session保存在memcached中,需要保证java正常启动不报错,openjdk和tomcat7组合使用会报错,tomcat7和openjdk8组合使用正常。