转载自:https://blog.csdn.net/qq_38182963/article/details/78660787
前言
这是我们分析 Tomcat 的第七篇文章,前面我们依据启动过程理解了类加载过程,生命周期组件,容器组件等。基本上将启动过程拆的七零八落,分析的差不多了, 但是还没有从整体的视图下来分析Tomcat 的启动过程。因此,这篇文章的任务就是这个,我们想将Tomcat的启动过程彻底的摸清,把它最后一件衣服扒掉。然后我们就分析连接器和URL请求了,不再留恋这里了。
好吧。我们开始吧。
说到Tomcat的启动,我们都知道,我们每次需要运行tomcat/bin/startup.sh这个脚本,而这个脚本的内容到底是什么呢?我们来看看。
1. startup.sh 脚本内容
#!/bin/sh
os400=false
case "`uname`" in
OS400*) os400=true;;
esac
# resolve links - $0 may be a softlink
PRG="$0"
while [ -h "
P
R
G
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
d
o
<
/
s
p
a
n
>
l
s
=
‘
l
s
−
l
d
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
PRG</span>"</span> ] ; <span class="hljs-keyword">do</span> ls=`ls -ld <span class="hljs-string">"<span class="hljs-variable">
PRG</span>"</span>];<spanclass="hljs−keyword">do</span>ls=‘ls−ld<spanclass="hljs−string">"<spanclass="hljs−variable">PRG"link=
expr "KaTeX parse error: Expected 'EOF', got '&' at position 51: …js-string">'.*-&̲gt; \(.*\)'<span class="hljs-keyword">if</span> expr <span class="hljs-string">"<span class="hljs-variable">$link</span>"</span> : <span class="hljs-string">'/.*'</span> > /dev/null; <span class="hljs-keyword">then</span> PRG=<span class="hljs-string">"<span class="hljs-variable">$link</span>"</span> <span class="hljs-keyword">else</span> PRG=
dirname “
P
R
G
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
‘
/
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
PRG</span>"</span>`/<span class="hljs-string">"<span class="hljs-variable">
PRG</span>"</span>‘/<spanclass="hljs−string">"<spanclass="hljs−variable">link”
fi
done
PRGDIR=dirname <span class="hljs-string">"<span class="hljs-variable">$PRG</span>"</span>
EXECUTABLE=catalina.sh
# Check that target executable exists
if KaTeX parse error: Expected 'EOF', got '#' at position 83: …"hljs-comment">#̲ -x will Only w…PRGDIR"/“
E
X
E
C
U
T
A
B
L
E
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
t
h
e
n
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
b
u
i
l
t
i
n
"
>
e
c
h
o
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
C
a
n
n
o
t
f
i
n
d
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
EXECUTABLE</span>"</span> ]; <span class="hljs-keyword">then</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"Cannot find <span class="hljs-variable">
EXECUTABLE</span>"</span>];<spanclass="hljs−keyword">then</span><spanclass="hljs−builtin">echo</span><spanclass="hljs−string">"Cannotfind<spanclass="hljs−variable">PRGDIR/$EXECUTABLE”
echo “The file is absent or does not have execute permission”
echo “This file is needed to run this program”
exit 1
fi
fi
exec “
P
R
G
D
I
R
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
/
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
PRGDIR</span>"</span>/<span class="hljs-string">"<span class="hljs-variable">
PRGDIR</span>"</span>/<spanclass="hljs−string">"<spanclass="hljs−variable">EXECUTABLE” start “$@”
楼主删除了一些无用的注释,我们来看看这脚本。该脚本中有2个重要的变量:
1. PRGDIR:表示当前脚本所在的路径
2. EXECUTABLE:catalina.sh 脚本名称
其中最关键的一行代码就是 exec "$PRGDIR"/"$EXECUTABLE" start "$@"
,表示执行了脚本catalina.sh,参数是start。
2. catalina.sh 脚本实现
然后我们看看catalina.sh 脚本中的实现:
elif [ "$1" = "start" ] ; then
if [ ! -z “
C
A
T
A
L
I
N
A
P
I
D
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
t
h
e
n
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
i
f
<
/
s
p
a
n
>
[
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
o
p
e
r
a
t
o
r
"
>
−
f
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
CATALINA_PID</span>"</span> ]; <span class="hljs-keyword">then</span> <span class="hljs-keyword">if</span> [ <span class="hljs-operator">-f</span> <span class="hljs-string">"<span class="hljs-variable">
CATALINAPID</span>"</span>];<spanclass="hljs−keyword">then</span><spanclass="hljs−keyword">if</span>[<spanclass="hljs−operator">−f</span><spanclass="hljs−string">"<spanclass="hljs−variable">CATALINA_PID” ]; then
if [ -s “
C
A
T
A
L
I
N
A
P
I
D
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
t
h
e
n
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
b
u
i
l
t
i
n
"
>
e
c
h
o
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
E
x
i
s
t
i
n
g
P
I
D
f
i
l
e
f
o
u
n
d
d
u
r
i
n
g
s
t
a
r
t
.
"
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
i
f
<
/
s
p
a
n
>
[
−
r
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
CATALINA_PID</span>"</span> ]; <span class="hljs-keyword">then</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"Existing PID file found during start."</span> <span class="hljs-keyword">if</span> [ -r <span class="hljs-string">"<span class="hljs-variable">
CATALINAPID</span>"</span>];<spanclass="hljs−keyword">then</span><spanclass="hljs−builtin">echo</span><spanclass="hljs−string">"ExistingPIDfilefoundduringstart."</span><spanclass="hljs−keyword">if</span>[−r<spanclass="hljs−string">"<spanclass="hljs−variable">CATALINA_PID” ]; then
PID=cat <span class="hljs-string">"<span class="hljs-variable">$CATALINA_PID</span>"</span>
ps -p $PID >/dev/null 2>&1
if [
?
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
o
p
e
r
a
t
o
r
"
>
−
e
q
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
n
u
m
b
e
r
"
>
0
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
t
h
e
n
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
b
u
i
l
t
i
n
"
>
e
c
h
o
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
T
o
m
c
a
t
a
p
p
e
a
r
s
t
o
s
t
i
l
l
b
e
r
u
n
n
i
n
g
w
i
t
h
P
I
D
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
? <span class="hljs-operator">-eq</span> <span class="hljs-number">0</span> ] ; <span class="hljs-keyword">then</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"Tomcat appears to still be running with PID <span class="hljs-variable">
?<spanclass="hljs−operator">−eq</span><spanclass="hljs−number">0</span>];<spanclass="hljs−keyword">then</span><spanclass="hljs−builtin">echo</span><spanclass="hljs−string">"TomcatappearstostillberunningwithPID<spanclass="hljs−variable">PID. Start aborted."
echo “If the following process is not a Tomcat process, remove the PID file and try again:”
ps -f -p
P
I
D
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
e
x
i
t
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
n
u
m
b
e
r
"
>
1
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
e
l
s
e
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
b
u
i
l
t
i
n
"
>
e
c
h
o
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
R
e
m
o
v
i
n
g
/
c
l
e
a
r
i
n
g
s
t
a
l
e
P
I
D
f
i
l
e
.
"
<
/
s
p
a
n
>
r
m
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
o
p
e
r
a
t
o
r
"
>
−
f
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
PID</span> <span class="hljs-keyword">exit</span> <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"Removing/clearing stale PID file."</span> rm <span class="hljs-operator">-f</span> <span class="hljs-string">"<span class="hljs-variable">
PID</span><spanclass="hljs−keyword">exit</span><spanclass="hljs−number">1</span><spanclass="hljs−keyword">else</span><spanclass="hljs−builtin">echo</span><spanclass="hljs−string">"Removing/clearingstalePIDfile."</span>rm<spanclass="hljs−operator">−f</span><spanclass="hljs−string">"<spanclass="hljs−variable">CATALINA_PID" >/dev/null 2>&1
if [
?
!
=
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
n
u
m
b
e
r
"
>
0
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
t
h
e
n
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
i
f
<
/
s
p
a
n
>
[
−
w
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
? != <span class="hljs-number">0</span> ]; <span class="hljs-keyword">then</span> <span class="hljs-keyword">if</span> [ -w <span class="hljs-string">"<span class="hljs-variable">
?!=<spanclass="hljs−number">0</span>];<spanclass="hljs−keyword">then</span><spanclass="hljs−keyword">if</span>[−w<spanclass="hljs−string">"<spanclass="hljs−variable">CATALINA_PID" ]; then
cat /dev/null > “
C
A
T
A
L
I
N
A
P
I
D
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
e
l
s
e
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
b
u
i
l
t
i
n
"
>
e
c
h
o
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
U
n
a
b
l
e
t
o
r
e
m
o
v
e
o
r
c
l
e
a
r
s
t
a
l
e
P
I
D
f
i
l
e
.
S
t
a
r
t
a
b
o
r
t
e
d
.
"
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
e
x
i
t
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
n
u
m
b
e
r
"
>
1
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
f
i
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
f
i
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
f
i
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
e
l
s
e
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
b
u
i
l
t
i
n
"
>
e
c
h
o
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
U
n
a
b
l
e
t
o
r
e
a
d
P
I
D
f
i
l
e
.
S
t
a
r
t
a
b
o
r
t
e
d
.
"
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
e
x
i
t
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
n
u
m
b
e
r
"
>
1
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
f
i
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
e
l
s
e
<
/
s
p
a
n
>
r
m
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
o
p
e
r
a
t
o
r
"
>
−
f
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
CATALINA_PID</span>"</span> <span class="hljs-keyword">else</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"Unable to remove or clear stale PID file. Start aborted."</span> <span class="hljs-keyword">exit</span> <span class="hljs-number">1</span> <span class="hljs-keyword">fi</span> <span class="hljs-keyword">fi</span> <span class="hljs-keyword">fi</span> <span class="hljs-keyword">else</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"Unable to read PID file. Start aborted."</span> <span class="hljs-keyword">exit</span> <span class="hljs-number">1</span> <span class="hljs-keyword">fi</span> <span class="hljs-keyword">else</span> rm <span class="hljs-operator">-f</span> <span class="hljs-string">"<span class="hljs-variable">
CATALINAPID</span>"</span><spanclass="hljs−keyword">else</span><spanclass="hljs−builtin">echo</span><spanclass="hljs−string">"UnabletoremoveorclearstalePIDfile.Startaborted."</span><spanclass="hljs−keyword">exit</span><spanclass="hljs−number">1</span><spanclass="hljs−keyword">fi</span><spanclass="hljs−keyword">fi</span><spanclass="hljs−keyword">fi</span><spanclass="hljs−keyword">else</span><spanclass="hljs−builtin">echo</span><spanclass="hljs−string">"UnabletoreadPIDfile.Startaborted."</span><spanclass="hljs−keyword">exit</span><spanclass="hljs−number">1</span><spanclass="hljs−keyword">fi</span><spanclass="hljs−keyword">else</span>rm<spanclass="hljs−operator">−f</span><spanclass="hljs−string">"<spanclass="hljs−variable">CATALINA_PID” >/dev/null 2>&1
if [
?
!
=
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
n
u
m
b
e
r
"
>
0
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
t
h
e
n
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
i
f
<
/
s
p
a
n
>
[
!
−
w
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
? != <span class="hljs-number">0</span> ]; <span class="hljs-keyword">then</span> <span class="hljs-keyword">if</span> [ ! -w <span class="hljs-string">"<span class="hljs-variable">
?!=<spanclass="hljs−number">0</span>];<spanclass="hljs−keyword">then</span><spanclass="hljs−keyword">if</span>[!−w<spanclass="hljs−string">"<spanclass="hljs−variable">CATALINA_PID" ]; then
echo “Unable to remove or write to empty PID file. Start aborted.”
exit 1
fi
fi
fi
fi
fi
shift
touch “$CATALINA_OUT”
if [ “
1
<
/
s
p
a
n
>
"
<
/
s
p
a
n
>
=
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
s
t
r
i
n
g
"
>
"
−
s
e
c
u
r
i
t
y
"
<
/
s
p
a
n
>
]
;
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
t
h
e
n
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
k
e
y
w
o
r
d
"
>
i
f
<
/
s
p
a
n
>
[
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
1</span>"</span> = <span class="hljs-string">"-security"</span> ] ; <span class="hljs-keyword">then</span> <span class="hljs-keyword">if</span> [ <span class="hljs-variable">
1</span>"</span>=<spanclass="hljs−string">"−security"</span>];<spanclass="hljs−keyword">then</span><spanclass="hljs−keyword">if</span>[<spanclass="hljs−variable">have_tty -eq 1 ]; then
echo “Using Security Manager”
fi
shift
eval KaTeX parse error: Can't use function '\"' in math mode at position 42: …"hljs-string">"\̲"̲<span class="hl…_RUNJAVA”" ““KaTeX parse error: Can't use function '\"' in math mode at position 22: …G_CONFIG</span>\̲"̲"</span> <span …LOGGING_MANAGER
J
A
V
A
O
P
T
S
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
JAVA_OPTS</span> <span class="hljs-variable">
JAVAOPTS</span><spanclass="hljs−variable">CATALINA_OPTS
-classpath ““KaTeX parse error: Can't use function '\"' in math mode at position 17: …LASSPATH</span>\̲"̲"</span> \ …CATALINA_BASE/conf/catalina.policy””
-Dcatalina.base=”“KaTeX parse error: Can't use function '\"' in math mode at position 21: …INA_BASE</span>\̲"̲"</span> \ …CATALINA_HOME””
-Djava.io.tmpdir=""KaTeX parse error: Can't use function '\"' in math mode at position 23: …A_TMPDIR</span>\̲"̲"</span> \ …@" start
>> “$CATALINA_OUT” 2>&1 “&”
else
eval KaTeX parse error: Can't use function '\"' in math mode at position 42: …"hljs-string">"\̲"̲<span class="hl…_RUNJAVA"" ““KaTeX parse error: Can't use function '\"' in math mode at position 22: …G_CONFIG</span>\̲"̲"</span> <span …LOGGING_MANAGER
J
A
V
A
O
P
T
S
<
/
s
p
a
n
>
<
s
p
a
n
c
l
a
s
s
=
"
h
l
j
s
−
v
a
r
i
a
b
l
e
"
>
JAVA_OPTS</span> <span class="hljs-variable">
JAVAOPTS</span><spanclass="hljs−variable">CATALINA_OPTS
-classpath ““KaTeX parse error: Can't use function '\"' in math mode at position 17: …LASSPATH</span>\̲"̲"</span> \ …CATALINA_BASE””
-Dcatalina.home=”“KaTeX parse error: Can't use function '\"' in math mode at position 21: …INA_HOME</span>\̲"̲"</span> \ …CATALINA_TMPDIR””
org.apache.catalina.startup.Bootstrap “KaTeX parse error: Expected 'EOF', got '\ ' at position 24: …"</span> start \̲ ̲ >> …CATALINA_OUT” 2>&1 “&”
fi
if [ ! -z “$CATALINA_PID” ]; then
echo KaTeX parse error: Expected 'EOF', got '&' at position 3: ! &̲gt; <span class…CATALINA_PID"
fi
echo “Tomcat started.”
该脚本很长,但我们只关心我们感兴趣的:如果参数是 start
, 那么执行这里的逻辑,关键再最后一行执行了 org.apache.catalina.startup.Bootstrap "$@" start
, 也就是说,执行了我们熟悉的main方法,并且携带了start 参数,那么我们就来看Bootstrap 的main方法是如何实现的。
3. Bootstrap.main 方法实现
public static void main(String args[]) {
System.err.println(<span class="hljs-string">"Have fun and Enjoy! cxs"</span>);
<span class="hljs-comment">// daemon 就是 bootstrap</span>
<span class="hljs-keyword">if</span> (daemon == <span class="hljs-keyword">null</span>) {
Bootstrap bootstrap = <span class="hljs-keyword">new</span> Bootstrap();
<span class="hljs-keyword">try</span> {
bootstrap.init();
} <span class="hljs-keyword">catch</span> (Throwable t) {
handleThrowable(t);
t.printStackTrace();
<span class="hljs-keyword">return</span>;
}
daemon = bootstrap;
} <span class="hljs-keyword">else</span> {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
<span class="hljs-keyword">try</span> {
String command = <span class="hljs-string">"start"</span>;
<span class="hljs-keyword">if</span> (args.length > <span class="hljs-number">0</span>) {
command = args[args.length - <span class="hljs-number">1</span>];
}
<span class="hljs-keyword">if</span> (command.equals(<span class="hljs-string">"startd"</span>)) {
args[args.length - <span class="hljs-number">1</span>] = <span class="hljs-string">"start"</span>;
daemon.load(args);
daemon.start();
}
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (command.equals(<span class="hljs-string">"stopd"</span>)) {
args[args.length - <span class="hljs-number">1</span>] = <span class="hljs-string">"stop"</span>;
daemon.stop();
}
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (command.equals(<span class="hljs-string">"start"</span>)) {
daemon.setAwait(<span class="hljs-keyword">true</span>);
daemon.load(args);
daemon.start();
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (command.equals(<span class="hljs-string">"stop"</span>)) {
daemon.stopServer(args);
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (command.equals(<span class="hljs-string">"configtest"</span>)) {
daemon.load(args);
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">null</span>==daemon.getServer()) {
System.exit(<span class="hljs-number">1</span>);
}
System.exit(<span class="hljs-number">0</span>);
} <span class="hljs-keyword">else</span> {
log.warn(<span class="hljs-string">"Bootstrap: command \""</span> + command + <span class="hljs-string">"\" does not exist."</span>);
}
} <span class="hljs-keyword">catch</span> (Throwable t) {
<span class="hljs-keyword">if</span> (t <span class="hljs-keyword">instanceof</span> InvocationTargetException &&
t.getCause() != <span class="hljs-keyword">null</span>) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(<span class="hljs-number">1</span>);
}
}</code></pre>
我们看看该方法, 首先 bootstrap.init()
的方法用于初始化类加载器,我们已经分析过该方法了,就不再赘述了,然后我们看下面的try块,默认命令行参数是 start
,但我们刚刚的脚本传的参数就是 start, 因此进入该if块
else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
- 设置catalina 的 await 属性为true;
- 运行 catalina 的 load 方法。该方法内部主要逻辑是解析server.xml文件,初始化容器。我们已经再生命周期那篇文章中讲过容器的初始化。
- 运行 catalina 的 start 方法。也就是启动 tomcat。这个部分我们上次分析了容器启动。但是容器之后的逻辑我们没有分析。今天我们就来看看。
4. Catalina.start 方法
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
<span class="hljs-comment">// If JULI is being used, disable JULI's shutdown hook since</span>
<span class="hljs-comment">// shutdown hooks run in parallel and log messages may be lost</span>
<span class="hljs-comment">// if JULI's hook completes before the CatalinaShutdownHook()</span>
LogManager logManager = LogManager.getLogManager();
<span class="hljs-keyword">if</span> (logManager <span class="hljs-keyword">instanceof</span> ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
<span class="hljs-keyword">false</span>);
}
}
<span class="hljs-keyword">if</span> (await) {
await();
stop();
}
}</code></pre>
该方法我们上次分析到了 getServer().start()
这里,也就是容器启动的逻辑,我们不再赘述。
今天我们继续分析下面的逻辑。主要逻辑是:
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
<span class="hljs-comment">// If JULI is being used, disable JULI's shutdown hook since</span>
<span class="hljs-comment">// shutdown hooks run in parallel and log messages may be lost</span>
<span class="hljs-comment">// if JULI's hook completes before the CatalinaShutdownHook()</span>
LogManager logManager = LogManager.getLogManager();
<span class="hljs-keyword">if</span> (logManager <span class="hljs-keyword">instanceof</span> ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
<span class="hljs-keyword">false</span>);
}
}
<span class="hljs-keyword">if</span> (await) {
await();
stop();
}</code></pre>
可以看到是 Runtime.getRuntime().addShutdownHook(shutdownHook)
方法。那么这个方法的作用是什么呢?JDK 文档是这样说的:
注册新的虚拟机来关闭钩子。
只是一个已初始化但尚未启动的线程。虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,并让它们同时运行。运行完所有的钩子后,如果已启用退出终结,那么虚拟机接着会运行所有未调用的终结方法。最后,虚拟机会暂停。注意,关闭序列期间会继续运行守护线程,如果通过调用方法来发起关闭序列,那么也会继续运行非守护线程。
简单来说,如果用户的程序出现了bug, 或者使用control + C 关闭了命令行,那么就需要做一些内存清理的工作。该方法就会再虚拟机退出时做清理工作。再ApplicationShutdownHooks 类种维护着一个IdentityHashMap
5. CatalinaShutdownHook.run 线程方法实现
protected class CatalinaShutdownHook extends Thread {
@Override
public void run() {
try {
if (getServer() != null) {
Catalina.this.stop();
}
} catch (Throwable ex) {
ExceptionUtils.handleThrowable(ex);
log.error(sm.getString("catalina.shutdownHookFail"), ex);
} finally {
// If JULI is used, shut JULI down *after* the server shuts down
// so log messages aren't lost
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).shutdown();
}
}
}
}
该线程是Catalina的内部类,方法逻辑是,如果Server容器还存在,就是执行Catalina的stop方法用于停止容器。(为什么要用Catalina.this.stop 呢?因为它继承了Thread,而Thread也有一个stop方法,因此需要显式的指定该方法)最后关闭日志管理器。我们看看stop方法的实现:
6. Catalina.stop 方法实现:
public void stop() {
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">// Remove the ShutdownHook first so that server.stop()</span>
<span class="hljs-comment">// doesn't get invoked twice</span>
<span class="hljs-keyword">if</span> (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
<span class="hljs-comment">// If JULI is being used, re-enable JULI's shutdown to ensure</span>
<span class="hljs-comment">// log messages are not lost</span>
LogManager logManager = LogManager.getLogManager();
<span class="hljs-keyword">if</span> (logManager <span class="hljs-keyword">instanceof</span> ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
<span class="hljs-keyword">true</span>);
}
}
} <span class="hljs-keyword">catch</span> (Throwable t) {
ExceptionUtils.handleThrowable(t);
<span class="hljs-comment">// This will fail on JDK 1.2. Ignoring, as Tomcat can run</span>
<span class="hljs-comment">// fine without the shutdown hook.</span>
}
<span class="hljs-comment">// Shut down the server</span>
<span class="hljs-keyword">try</span> {
Server s = getServer();
LifecycleState state = s.getState();
<span class="hljs-keyword">if</span> (LifecycleState.STOPPING_PREP.compareTo(state) <= <span class="hljs-number">0</span>
&& LifecycleState.DESTROYED.compareTo(state) >= <span class="hljs-number">0</span>) {
<span class="hljs-comment">// Nothing to do. stop() was already called</span>
} <span class="hljs-keyword">else</span> {
s.stop();
s.destroy();
}
} <span class="hljs-keyword">catch</span> (LifecycleException e) {
log.error(<span class="hljs-string">"Catalina.stop"</span>, e);
}
}</code></pre>
该方法首先移除关闭钩子,为什么要移除呢,因为他的任务已经完成了。然后设置useShutdownHook 为true。最后执行Server的stop方法,Server的stop方法基本和init方法和start方法一样,都是使用父类的模板方法,首先出发事件,然后调用stopInternal,该方法内部循环停止子容器,子容器递归停止,和我们之前的逻辑一致,不再赘述。destroy方法同理。
好了,我们已经看清了关闭钩子的逻辑,其实就是开辟一个守护线程交给虚拟机,然后虚拟机在某些异常情况(比如System.exit(0))前执行停止容器的逻辑。
好。我们回到start方法。
7. 回到 Catalina.start 方法
在设置好关闭钩子后,tomcat 的启动过程还没有启动完毕,接下来的逻辑式什么呢?
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
<span class="hljs-comment">// If JULI is being used, disable JULI's shutdown hook since</span>
<span class="hljs-comment">// shutdown hooks run in parallel and log messages may be lost</span>
<span class="hljs-comment">// if JULI's hook completes before the CatalinaShutdownHook()</span>
LogManager logManager = LogManager.getLogManager();
<span class="hljs-keyword">if</span> (logManager <span class="hljs-keyword">instanceof</span> ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
<span class="hljs-keyword">false</span>);
}
}
<span class="hljs-keyword">if</span> (await) {
await();
stop();
}</code></pre>
在设置完关闭钩子之后,会将 useShutdownHook 这个变量为false,然后执行 await 方法。然后执行stop方法,我们记得stop方法式关闭容器的方法,神经病啊,好不容易启动了,为什么又要关闭呢? 先不着急,我们还是看看 await 方法吧,该方法调用了Server.await 方法,我们来看看:
8. Catalian.await 方法实现
注意:该方法很长
@Override
public void await() {
// Negative values - don't wait on port - tomcat is embedded or we just don't like ports
if( port == -2 ) {
// undocumented yet - for embedding apps that are around, alive.
return;
}
if( port==-1 ) {
try {
awaitThread = Thread.currentThread();
while(!stopAwait) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
// continue and check the flag
}
}
} finally {
awaitThread = null;
}
return;
}
<span class="hljs-comment">// Set up a server socket to wait on</span>
<span class="hljs-keyword">try</span> {
awaitSocket = <span class="hljs-keyword">new</span> ServerSocket(port, <span class="hljs-number">1</span>,
InetAddress.getByName(address));
} <span class="hljs-keyword">catch</span> (IOException e) {
log.error(<span class="hljs-string">"StandardServer.await: create["</span> + address
+ <span class="hljs-string">":"</span> + port
+ <span class="hljs-string">"]: "</span>, e);
<span class="hljs-keyword">return</span>;
}
<span class="hljs-keyword">try</span> {
awaitThread = Thread.currentThread();
<span class="hljs-comment">// Loop waiting for a connection and a valid command</span>
<span class="hljs-keyword">while</span> (!stopAwait) {
ServerSocket serverSocket = awaitSocket;
<span class="hljs-keyword">if</span> (serverSocket == <span class="hljs-keyword">null</span>) {
<span class="hljs-keyword">break</span>;
}
<span class="hljs-comment">// Wait for the next connection</span>
Socket socket = <span class="hljs-keyword">null</span>;
StringBuilder command = <span class="hljs-keyword">new</span> StringBuilder();
<span class="hljs-keyword">try</span> {
InputStream stream;
<span class="hljs-keyword">try</span> {
socket = serverSocket.accept();
socket.setSoTimeout(<span class="hljs-number">10</span> * <span class="hljs-number">1000</span>); <span class="hljs-comment">// Ten seconds</span>
stream = socket.getInputStream();
} <span class="hljs-keyword">catch</span> (AccessControlException ace) {
log.warn(<span class="hljs-string">"StandardServer.accept security exception: "</span>
+ ace.getMessage(), ace);
<span class="hljs-keyword">continue</span>;
} <span class="hljs-keyword">catch</span> (IOException e) {
<span class="hljs-keyword">if</span> (stopAwait) {
<span class="hljs-comment">// Wait was aborted with socket.close()</span>
<span class="hljs-keyword">break</span>;
}
log.error(<span class="hljs-string">"StandardServer.await: accept: "</span>, e);
<span class="hljs-keyword">break</span>;
}
<span class="hljs-comment">// Read a set of characters from the socket</span>
<span class="hljs-keyword">int</span> expected = <span class="hljs-number">1024</span>; <span class="hljs-comment">// Cut off to avoid DoS attack</span>
<span class="hljs-keyword">while</span> (expected < shutdown.length()) {
<span class="hljs-keyword">if</span> (random == <span class="hljs-keyword">null</span>)
random = <span class="hljs-keyword">new</span> Random();
expected += (random.nextInt() % <span class="hljs-number">1024</span>);
}
<span class="hljs-keyword">while</span> (expected > <span class="hljs-number">0</span>) {
<span class="hljs-keyword">int</span> ch = -<span class="hljs-number">1</span>;
<span class="hljs-keyword">try</span> {
ch = stream.read();
} <span class="hljs-keyword">catch</span> (IOException e) {
log.warn(<span class="hljs-string">"StandardServer.await: read: "</span>, e);
ch = -<span class="hljs-number">1</span>;
}
<span class="hljs-keyword">if</span> (ch < <span class="hljs-number">32</span>) <span class="hljs-comment">// Control character or EOF terminates loop</span>
<span class="hljs-keyword">break</span>;
command.append((<span class="hljs-keyword">char</span>) ch);
expected--;
}
} <span class="hljs-keyword">finally</span> {
<span class="hljs-comment">// Close the socket now that we are done with it</span>
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">if</span> (socket != <span class="hljs-keyword">null</span>) {
socket.close();
}
} <span class="hljs-keyword">catch</span> (IOException e) {
<span class="hljs-comment">// Ignore</span>
}
}
<span class="hljs-comment">// Match against our command string</span>
<span class="hljs-keyword">boolean</span> match = command.toString().equals(shutdown);
<span class="hljs-keyword">if</span> (match) {
log.info(sm.getString(<span class="hljs-string">"standardServer.shutdownViaPort"</span>));
<span class="hljs-keyword">break</span>;
} <span class="hljs-keyword">else</span>
log.warn(<span class="hljs-string">"StandardServer.await: Invalid command '"</span>
+ command.toString() + <span class="hljs-string">"' received"</span>);
}
} <span class="hljs-keyword">finally</span> {
ServerSocket serverSocket = awaitSocket;
awaitThread = <span class="hljs-keyword">null</span>;
awaitSocket = <span class="hljs-keyword">null</span>;
<span class="hljs-comment">// Close the server socket and return</span>
<span class="hljs-keyword">if</span> (serverSocket != <span class="hljs-keyword">null</span>) {
<span class="hljs-keyword">try</span> {
serverSocket.close();
} <span class="hljs-keyword">catch</span> (IOException e) {
<span class="hljs-comment">// Ignore</span>
}
}
}
}
我们看一下他的逻辑:首先创建一个socketServer 链接,然后循环等待消息。如果发过来的消息为字符串SHUTDOWN
, 那么就break,停止循环,关闭socket。否则永不停歇。回到我们刚刚的疑问,await 方法后面执行 stop 方法,现在一看就合情合理了,只要不发出关闭命令,则不会执行stop方法,否则则继续执行关闭方法。
到现在,Tomcat 的整体启动过程我们已经了然于胸了,总结一下就是:
1. 初始化类加载器。
2. 初始化容器并注册到JMX后启动容器。
3. 设置关闭钩子。
4. 循环等待关闭命令。
等一下。好像缺了点什么??? Tomcat 启动后就只接受关闭命令,接受的http请求怎么处理,还要不要做一个合格的服务器了??? 别急,实际上,这个是主线程,负责生命周期等事情。处理Http请求的线程在初始化容器和启动容器的时候由子容器做了,这块的逻辑我们下次再讲。大家不要疑惑。
9. 我们知道了Tomcat 是怎么启动的,那么是怎么关闭的呢?
顺便说说关闭的逻辑:
shutdown.sh 脚本同样会调用 Bootstrap的main 方法,不同是传递 stop参数, 我们看看如果传递stop参数会怎么样:
ry {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
}
else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
}
else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
可以看到调用的是 stopServer 方法,实际上就是 Catalina的stopServer 方法,我们看看该方法实现:
10. Catalina.stopServer 方法
public void stopServer(String[] arguments) {
<span class="hljs-keyword">if</span> (arguments != <span class="hljs-keyword">null</span>) {
arguments(arguments);
}
Server s = getServer();
<span class="hljs-keyword">if</span>( s == <span class="hljs-keyword">null</span> ) {
<span class="hljs-comment">// Create and execute our Digester</span>
Digester digester = createStopDigester();
digester.setClassLoader(Thread.currentThread().getContextClassLoader());
File file = configFile();
FileInputStream fis = <span class="hljs-keyword">null</span>;
<span class="hljs-keyword">try</span> {
InputSource is =
<span class="hljs-keyword">new</span> InputSource(file.toURI().toURL().toString());
fis = <span class="hljs-keyword">new</span> FileInputStream(file);
is.setByteStream(fis);
digester.push(<span class="hljs-keyword">this</span>);
digester.parse(is);
} <span class="hljs-keyword">catch</span> (Exception e) {
log.error(<span class="hljs-string">"Catalina.stop: "</span>, e);
System.exit(<span class="hljs-number">1</span>);
} <span class="hljs-keyword">finally</span> {
<span class="hljs-keyword">if</span> (fis != <span class="hljs-keyword">null</span>) {
<span class="hljs-keyword">try</span> {
fis.close();
} <span class="hljs-keyword">catch</span> (IOException e) {
<span class="hljs-comment">// Ignore</span>
}
}
}
} <span class="hljs-keyword">else</span> {
<span class="hljs-comment">// Server object already present. Must be running as a service</span>
<span class="hljs-keyword">try</span> {
s.stop();
} <span class="hljs-keyword">catch</span> (LifecycleException e) {
log.error(<span class="hljs-string">"Catalina.stop: "</span>, e);
}
<span class="hljs-keyword">return</span>;
}
<span class="hljs-comment">// Stop the existing server</span>
s = getServer();
<span class="hljs-keyword">if</span> (s.getPort()><span class="hljs-number">0</span>) {
Socket socket = <span class="hljs-keyword">null</span>;
OutputStream stream = <span class="hljs-keyword">null</span>;
<span class="hljs-keyword">try</span> {
socket = <span class="hljs-keyword">new</span> Socket(s.getAddress(), s.getPort());
stream = socket.getOutputStream();
String shutdown = s.getShutdown();
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < shutdown.length(); i++) {
stream.write(shutdown.charAt(i));
}
stream.flush();
} <span class="hljs-keyword">catch</span> (ConnectException ce) {
log.error(sm.getString(<span class="hljs-string">"catalina.stopServer.connectException"</span>,
s.getAddress(),
String.valueOf(s.getPort())));
log.error(<span class="hljs-string">"Catalina.stop: "</span>, ce);
System.exit(<span class="hljs-number">1</span>);
} <span class="hljs-keyword">catch</span> (IOException e) {
log.error(<span class="hljs-string">"Catalina.stop: "</span>, e);
System.exit(<span class="hljs-number">1</span>);
} <span class="hljs-keyword">finally</span> {
<span class="hljs-keyword">if</span> (stream != <span class="hljs-keyword">null</span>) {
<span class="hljs-keyword">try</span> {
stream.close();
} <span class="hljs-keyword">catch</span> (IOException e) {
<span class="hljs-comment">// Ignore</span>
}
}
<span class="hljs-keyword">if</span> (socket != <span class="hljs-keyword">null</span>) {
<span class="hljs-keyword">try</span> {
socket.close();
} <span class="hljs-keyword">catch</span> (IOException e) {
<span class="hljs-comment">// Ignore</span>
}
}
}
} <span class="hljs-keyword">else</span> {
log.error(sm.getString(<span class="hljs-string">"catalina.stopServer"</span>));
System.exit(<span class="hljs-number">1</span>);
}
}
注意,该停止命令的虚拟机和启动的虚拟机不是一个虚拟机,因此,没有初始化 Server , 进入 IF 块,解析 server.xml 文件,获取文件中端口,用以创建Socket。然后像启动服务器发送 SHUTDOWN
命令,关闭启动服务器,启动服务器退出刚刚的循环,执行后面的 stop 方法,最后退出虚拟机,就是这么简单。
11. 总结
我们从整体上解析了Tomcat的启动和关闭过程,发现不是很难,为什么?因为我们之前已经分析过很多遍了,有些逻辑我们已经清除了,这次分析只是来扫尾。复杂的Tomcat的启动过程我们基本就分析完了。我们知道了启动和关闭都依赖Socket。只是我们惊奇的发现他的关闭竟然是如此实现。很牛逼。我原以为会像我们平时一样,直接kill。哈哈哈。
好吧。今天我们就到这里 ,tomcat 这座大山我们已经啃的差不多了,还剩一个 URL 请求过程和连接器,这两个部分是高度关联的,因此,楼主也会将他们放在一起分析。透过源码看真相。
连接器,等着我们来撕开你的衣服!!!!
good luck !!!!