https://blog.51cto.com/13570193/2165362
目錄
- 部署tomcat
- 主配置文件
- Nginx+Tomcat實現動靜分離
- Nginx+Tomcat+memcache實現高可用會話集羣
一、部署tomcat
第一步:安裝JDK。
tomcat在處理客戶端請求的jsp文件的時候,會將jsp文件中的java程序提取出來並且運行,java程序能夠運行是需要JDK的支持的。
目前jdk存在兩個分支,一個是Oracle公司負責維護的jdk,一個是開源界維護的openjdk,並且openjdk軟件在CentOS的光盤中會提供。
方法一:使用Oracle的分支,進入Oracle官網下載jdk10版本,網址是https://www.oracle.com/technetwork/java/javase/downloads/jdk10-downloads-4416644.html
會看到Linux相對應的兩個文件,一個.rpm文件,一個.tar.gz文件(綠色版),.rpm文件就是普通rpm軟件包的正常安裝方式即可,.tar.gz文件下載下來之後直接解壓至指定目錄就可以使用。
然後安裝,安裝之後的文件目錄會存放在/usr/java目錄下。
[root@Web1 src]# pwd
/usr/local/src
[root@Web1 src]# ls jdk-10.0.2_linux-x64_bin.rpm
jdk-10.0.2_linux-x64_bin.rpm
[root@Web1 src]# yum -y localinstall jdk-10.0.2_linux-x64_bin.rpm
[root@Web1 src]# cd /usr/java/
[root@Web1 java]# ls
default jdk-10.0.2 latest
可以看到生成了三個目錄,在jdk-10.0.2目錄下的bin目錄下存在一個java命令。需要將bin目錄下的命令配置到系統的PATH變量中。配置如下:
[root@Web1 ~]# vim /etc/profile.d/java.sh
export JAVA_HOME=/usr/java/jdk-10.0.2
export PATH=$JAVA_HOME/bin:$PATH
# 重載環境變量
[root@Web1 ~]# source /etc/profile.d/java.sh
[root@Web1 ~]# which java
/usr/java/jdk-10.0.2/bin/java
到此爲止,JDK的安裝和PATH變量的配置已經完成,可以使用java -version命令查看安裝的JDK版本。
方法二:使用openjdk分支,openjdk在CentOS的本地光盤中就會提供,因此使用yum直接安裝即可。
[root@Web2 ~ ]# yum list all *openjdk*
執行上面的命令之後,會看到很多openjdk版本和各個版本的各個組件,假如使用java-1.8.0-openjdk這個版本,另外還需要關注相對應版本的java-1.8.0-openjdk-devel這個組件。他們的關係如下:
java-1.8.0-openjdk java程序的運行環境
java-1.8.0-openjdk-devel java程序的編譯開發環境
僅安裝openjdk之後,只存在一個jre下的java命令,但是繼續安裝openjdk-devel之後,就會多出一個javac命令。
安裝這兩個組件,
[root@Web2 ~]# yum -y install java-1.8.0-openjdk.x86_64 java-1.8.0-openjdk-devel.x86_64
安裝後我們不知道bin目錄在什麼位置,這樣就無法配置系統的PATH變量。可以使用命令rpm來查看軟件包安裝後每個文件的位置:
[root@Web2 ~]# rpm -ql java-1.8.0-openjdk-devel |grep /bin$
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64/bin
配置PATH變量
[root@Web2 ~]# vim /etc/profile.d/java.sh
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64
export PATH=$JAVA_HOME/bin:$PATH
[root@Web2 ~]# source /etc/profile.d/java.sh
[root@Web2 ~]# which java
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64/bin/java
第二步:安裝tomcat
方法一:tomcat在CentOS的本地光盤中就會提供,因此直接yum倉庫安裝即可。
[root@Web2 ~]# yum -y install tomcat tomcat-admin-webapps tomcat-webapps
其中tomcat-admin-webapps是一個tomca應用程序t的管理組件,tomcat-webapps是一個提供tomcat的默認歡迎首頁文件的組件。
tomcat安裝完成之後,會提供一個管理命令/usr/sbin/tomcat,而且還會提供相應的tomcat服務(CentOS6--/etc/init.d/tomcat start、CentOS7--systemctl start tomcat)。
啓動tomcat
[root@Web2 ~]# /usr/sbin/tomcat start
tomcat默認工作在非特權用戶下,因此tomcat就無法監聽80端口,因此tomcat默認監聽8080端口。
測試
瀏覽器中輸入ip:8080,出現下圖界面表示tomcat啓動成功。
方法二:在tomcat的官網下載安裝。網址爲:https://tomcat.apache.org/
下載成功後如下,然後解壓至/usr/local目錄下就可以使用。
[root@Web1 src]# pwd
/usr/local/src
[root@Web1 src]# ls apache-tomcat-8.5.32.tar.gz
apache-tomcat-8.5.32.tar.gz
[root@Web1 src]# tar -zxvf apache-tomcat-8.5.32.tar.gz -C /usr/local/
[root@Web1 local]# ls -d /usr/local/apache-tomcat-8.5.32/
/usr/local/apache-tomcat-8.5.32/
這樣/usr/local目錄下的apache-tomcat-8.5.32太長,因此使用軟鏈接的方式,
[root@Web1 local]# pwd
/usr/local
[root@Web1 local]# ln -sv apache-tomcat-8.5.32/ tomcat8
解壓後的bin目錄下提供了一套tomcat的管理腳本,例如啓動tomcat的startup.sh,關閉tomcat的shutdown.sh,查看tomcat版本信息的version.sh等等。
二、主配置文件
tomcat的主配置文件server.xml文件的整體結構如下,而且文件結構也符合tomcat的架構層次。
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" />
<Connector />
...
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost">
...
</Host>
</Engine>
</Service>
</Server>
tomcat是java語言開發的,因此配置文件也相符合於面向對象的思想,例如,文件中的每個元素創建“對象”,併爲屬性賦值實例化對象。
該配置文件中的元素都是大寫字母開頭。
Server
配置文件中的頂層元素,代表一個tomcat實例,並且配置文件中僅能有一個Server。
屬性 | 備註 | 屬性值 |
---|---|---|
classname | 實現server的類名 | |
port | 接受關閉tomcat請求的端口,僅能綁定至127.0.0.1 | 8005 |
shutdown | 定義關閉tomcat的字符串指令 | SHUTDOWN |
Listener
表示一個事件的監聽器
屬性 | 備註 | 屬性值 |
---|---|---|
clsssname | 實現該監聽器的類名 | |
SSLEngine | 是否使用SSL | on或者off |
GlobalNamingResources
全局JNDI資源,該元素沒有任何屬性,所有的應用程序都可以引用全局JNDI資源。那什麼是JNDI資源呢?JNDI多用於java的數據庫連接中,到這裏我們首先要想到的是JDBC,在JDBC中,連接數據庫需要用戶名、用戶名密碼和數據庫名稱等參數,將來如果這三個參數中的任何一個修改,則整個程序中相關的地方都需要修改,簡直是牽一髮而動全身,因此出現JNDI技術,在JNDI中,將連接數據庫需要的用戶、用戶密碼和數據庫名稱等JDBC引用的參數定義爲一個整體(即這個整體中包含JDBC要用到類庫、數據庫驅動程序、用戶、用戶密碼等),然後爲這個整體設置一個名稱,這樣以後連接數據庫的時候直接在程序中引用這個整體的名稱即可,無論用戶、用戶密碼怎麼變換,只要這個整體的名稱不變,我們僅要修改這個整體的用戶和用戶密碼,而無需修改整個程序。這裏的這個整體就是JNDI數據源(JNDI資源)。
雖然GlobalNamingResources沒有定義任何的屬性,但是可以在<GlobalNamingResources>...</GlobalNamingResources>中嵌套Resource元素。
Resource
定義JNDI數據源
屬性 | 備註 | 默認值 |
---|---|---|
name | 定義的JNDI資源的名稱 | |
auth | 指定管理Resource的管理器 | 有Container和Application兩種 |
type | 使用的類名 | |
description | 描述信息 | --- |
示例:
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
示例表示定義了一個名爲UserDataBase的全局JNDI數據源,使用容器管理該Resource,該數據源爲加載tomcat-users.xml文件至內存中而定義的用於用戶授權的數據庫。然後在認證文件tomcat-users.xml中添加如下幾行。表示用戶名爲admin,用戶密碼爲centos的認證用戶分別以manager-gui和admin-gui的角色登錄manager和host-manager的應用程序。
Service
包含一個或多個Connector和一個Engine的服務組件。屬性name表示該Service的名稱,默認保持 Catalina即可。<Service>...</Service>中可以嵌套Connector元素和Engine元素。
Connector
代表一個接受客戶端請求的連接器。
屬性 | 備註 | 屬性值 |
---|---|---|
accepCount | 等待隊列的長度 | 10 |
port | 連接器監聽的端口 | |
protocol | 連接器應用的協議類型 | http/https/ajp |
connectionTimeout | 連接的超時時間,單位爲毫秒 | 20000 |
address | 連接器監聽的IP地址 | 默認爲所有地址,0.0.0.0 |
maxThreads | 最大併發連接數 | http連接器默認200,https連接器默認150 |
enableLookups | 是否開啓DNS反解 | true或false |
clientAuth | tomcat是否需要認證客戶端 | 默認false |
redirectPort | 重定向至指定端口的Connector | 8443 |
scheme | 如果爲SSL連接器,則爲https | http/https |
secure | SSL連接器則爲true | true或false |
SSLEnabled | 是否開啓SSL | true或false |
sslProtocol | TLS |
其中ajp協議應用在Apache在反向代理tomcat的場景,Apache和tomcat之間就是使用ajp協議通信,它是一種二進制協議。
Engine
代表一個servlet實例,用於處理Connector接受並傳遞過來的請求。
屬性 | 備註 | 屬性值 |
---|---|---|
name | 定義Engine的名稱 | Catalina |
defaultHost | 定義Engine的默認虛擬主機 | 默認爲localhost |
jvmRoute | 應用於tomcat集羣中的session共享,會在一次會話中添加該值,獲得session sticky | --- |
Host
嵌套在<Engine name="Catalina" defaultHost="localhost"> ... </Engine>中的元素,代表一個虛擬主機,一個Engine可以有多個Host。
屬性 | 備註 | 屬性值 |
---|---|---|
name | 虛擬主機名 | |
appBase | 虛擬主機的站點根目錄 | webapps |
unpackWARs | tomcat是否解開WAR歸檔文件,默認爲true,自動解開 | true或false |
autoDeploy | 是否自動部署追加到根目錄下的新應用程序,默認爲true | true或false |
示例:
在Engine中新嵌套一個Host,也就是新增加一個虛擬主機,配置內容如下
<Host name="Web1.linux.com" appBase="/data/webapps"
unpackWARS="true" autoDeploy="true" />
創建appBase目錄,並且tomcat虛擬主機中的每個應用程序都是單獨放在一個目錄內,因此創建ROOT目錄,
[root@Web1 ~]# mkdir -p /data/webapps/ROOT
放入jsp測試頁面
[root@Web1 ~]# vim /data/webapps/ROOT/index.jsp
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'first.jsp' starting page</title>
</head>
<body>
<!-- 在JSP的頁面中以表格的形式打印九九乘法表 -->
<h1>九九乘法表</h1>
<%
for(int i=1;i<=9;i++) {
for(int j=1;j<=i;j++) {
out.println(i+"*"+j+"="+(i*j)+" ");
}
out.println("<br/>");
}
%>
</body>
</html>
然後hosts文件中添加Web1.linux.com記錄,瀏覽器中訪問。
Realm
訪問應用程序的時候需要安全認證,即需要輸入用戶和用戶密碼,Realm就是指定存儲驗證信息(用戶和用戶密碼)的數據源。
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
表示通過UserDatabaseRealm的方式獲取驗證信息,並且該數據源爲GlobalNamingResources元素定義的全局JNDI資源UserDatebase。Realm元素嵌套的位置代表驗證的範圍,例如嵌套在Engine中,則Engine中所有的Host虛擬主機都共用這個認證信息。嵌套在Host中,則Host中所有的應用程序都共用這個認證信息。嵌套在Context中,則該應用程序使用這個認證信息。
Valve
可以嵌套在Engine、Host、Context元素中的組件,不同的類型可以實現特定的功能,例如AccessLogValve可以實現生成訪問日誌的功能,RemoteAddrValve可以實現控制遠程IP地址的訪問。
示例:
<Host name="Web1.linux.com" appBase="/data/webapps"
unpackWARS="true" autoDeploy="true">
<!--
directory表示日誌存放的目錄,prefix和suffix分別表示生成的日誌文件名的前綴和後綴,pattern表示記錄日誌的格式
-->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="Web1_access_log" suffix=".log"
pattern="%h %l %u %t "%r" %s %b" />
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
deny="192.168.239.1" />
</Host>
繼續訪問Web1.linux.com,結果沒有返回頁面內容,並且在logs目錄下生成了訪問日誌。
Context
嵌套在Host中的元素,代表某個虛擬主機中的應用程序。常用屬性如下
屬性 | 備註 |
---|---|
docBase | 對應的web應用程序的目錄 |
path | 指定該應用程序映射爲服務器根目錄的URI路徑,即host+path |
reloadable | 是否重新加載web應用程序類,默認爲false |
注:tomcat虛擬主機中的每個應用程序必須單獨存在一個目錄中,docBase可以使用相對路徑和絕對路徑,相對路徑是相對於Host虛擬主機的appBase目錄。
如果path屬性值爲空字符串,則表示該應用程序爲根web應用,即ROOT目錄。
示例:
<Host name="Web1.linux.com" appBase="/data/webapps"
unpackWARS="true" autoDeploy="true">
<Context path="/test" docBase="test" reloadable="true" />
</Host>
然後創建web應用程序的jsp代碼,URL地址列中輸入http://web1.linux.com:8080/test ,查看訪問結果。
[root@Web1 ~]# mkdir -p /data/webapps/test
[root@Web1 test]# vim index.jsp
<HTML>
<HEAD>
<TITLE>JSP test</TITLE>
</HEAD>
<BODY>
<%
out.println("<h1>Hello World!!</h1>");
%>
</BODY>
</HTML>
三、Nginx+Tomcat實現動靜分離
tomcat能夠處理用戶的web請求(包括動態資源和靜態資源),但是tomcat在處理靜態資源的時候性能並不是很好,更多的時候是讓tomcat只處理動態資源請求,因此使用tomcat大多是使用如下的架構:
在tomcat的前端加一個Nginx反向代理,Nginx接受用戶發來的Web請求,當請求的是靜態資源的時候,Nginx自己處理,當請求的動態資源的時候,則Nginx將請求代理到後面的tomcat實例。這樣來實現tomcat的性能最大化。
Nginx的反向代理配置如下:
當用戶請求的是除了jsp|do以外的靜態資源的時候,則Nginx自己到/data/webapps/html目錄下響應請求,但用戶請求的是jsp|do的動態資源的時候,則Nginx將請求代理到後邊的tomcat。因此來實現動靜分離。
[root@Web1 ~]# vim /etc/nginx/conf.d/proxy.conf
server {
listen 80;
server_name Web1.linux.com;
index index.jsp;
location / {
root /data/webapps/html;
expires 30d;
}
location ~* \.(jsp|do)$ {
proxy_pass http://localhost:8080;
}
}
tomcat的sever.xml的主要配置如下:
<Host name="localhost" appBase="/data/webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<Context path="" docBase="test" reloadable="true"/>
</Host>
然後在/data/webapps/html目錄下編輯一個測試靜態資源,在/data/webapps/test目錄下編輯一個測試jsp動態代碼。
[root@Web1 ~]# cat /data/webapps/html/index.html
<h2>This is static resource!</h2>
[root@Web1 ~]# cat /data/webapps/test/index.jsp
<%@ page language="java" %>
<HTML>
<HEAD>
<TITLE>JSP test</TITLE>
</HEAD>
<BODY>
<%
out.println("<h1>This is dynamic resource!</h1>");
%>
</BODY>
</HTML>
測試:
在瀏覽器中輸入Nginx的IP地址。
四、Nginx+Tomcat+memcached實現高可用會話集羣
通過前端負載均衡器調度到後端不同Tomcat服務器的時候,會出現session不一致的問題。爲了解決這個問題,目前實現會話保持的方式主要一下三種:
① stickysession,會話綁定,通過前端調度器特定的算法,將同一個客戶端總是調度到後端同一臺服務器,例如Nginx做負載均衡的ip_hash算法。
② session replication,會話複製,這個操作在服務器自身上邊進行,服務器之間通過複製會話來實現會話同步,例如Tomcat自帶的會話集羣功能Cluster。
③ session server,會話服務器,通過將session存儲在特定的主機之上,服務器通過統一地訪問session主機實現會話同步,例如redis、memcached就是這種會話服務器。
環境拓撲:
主機 | IP地址 | 功能 |
---|---|---|
Master.linux.com | 192.168.239.137 | 負載均衡調度器 |
Web1.linux.com | 192.168.239.129 | Nginx+tomcat1主機兼memcached1主機 |
Web2.linux.com | 192.168.239.140 | Nginx+tomcat2主機兼memcached2主機 |
注:Nginx+tomcat主機實現的tomcat的動靜分離。
第一步:安裝memcache
CentOS本地光盤中提供
[root@Web1 ~]# yum -y install memcached
[root@Web2 ~]# yum -y install memcached
然後開啓memcached服務,查看memcached的端口監聽狀態,
第二步:安裝tomcat的第三方類庫memcached-session-manager(簡稱msm)。
該項目在GitHub上。地址爲:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration
需要下載 memcached-session-manager-${version}.jar,memcached-session-manager-tc8-${version}.jar(具體的tc版本要和tomcat版本相同,catalina.sh腳本的 version選項可以查看tomcat的版本信息) 和 spymemcached-${version}.jar。
使用msm還依賴serializer序列化工具,msm提供了四種序列化工具,選擇其中一種即可。
這四種連接memcached的序列化工具,分別爲kryo-serializer,javolution-serializer,xstream-serializer,flexjson-serializer。
這裏以javolution-serializer爲例,其下的兩個小組件的下載地址如下:
http://www.java2s.com/Code/JarDownload/javolution/javolution-5.5.1.jar.zip
該文件需要解壓成jar文件。
http://repo1.maven.org/maven2/de/javakaffee/msm/msm-javolution-serializer/2.1.1/msm-javolution-serializer-2.1.1.jar
所有的jar文件(這裏是5個)下載完成之後,將這些jar文件複製到$CATALINA_BASE/lib目錄下,該目錄下存放着tomcat調用的類庫文件(jar文件),我這裏的CATALINA_BASE爲/usr/local/tomcat8,即安裝目錄。
第三步:配置Context
在兩個tomcat主機的server.xml指定的應用程序下邊(即Context),添加Manager元素。具體信息如下:
<Host name="localhost" appBase="/data/webapps"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="ROOT" reloadable="true">
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.169.239.129:11211,n2:192.168.239.140:11211"
failoverNodes="n1"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"/>
</Context>
</Host>
memcachedNodes屬性指定每個memcache的節點信息,格式爲<節點標誌符:hostname:port>;
failoverNodes屬性指定哪一個memcache節點爲備用節點;
requestUriIgnorePattern屬性指定無需將靜態資源實現會話保持,因爲會話本來就是針對動態資源的;
transcoderFactoryClass屬性指定序列化工具。
第四步:實現前端的Nginx負載均衡
[root@Master ~]# vim /usr/local/nginx/conf/conf.d/balance.conf
upstream webserver {
# tomcat1
server 192.168.239.129:80 weight=1;
# tomcat2
server 192.168.239.140:80 weight=2;
}
server {
listen 80;
server_name web.linux.com;
root /data/html;
index index.php index.html index.htm;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forward-For $remote_addr;
proxy_pass http://webserver;
}
}
到此整個配置已經完成,重啓tomcat重新加載新的server.xml文件。
添加測試代碼文件index.jsp。
[root@Web1 ~]# vim /data/webapps/ROOT/index.jsp
<%@ page language="java" %>
<html>
<head><title>Tomcat 1</title></head>
<body>
<h1><font color="red">Tomcat1.linux.com</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("linux.com","linux..com"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
在瀏覽器中輸入調度器的IP地址,一直刷新,可以看到調度到不同的tomcat,但是session卻保持不變。另外也可以看到連接的是n2的memcache。後邊的-n2表示session-id的後綴。
現在暫停n2的memcached服務,繼續刷新,session保持不變,但是連接的memcache變成了備用節點n1。實現了memcache的高可用。