tengine反向代理tomcat多實例實現負載均衡

目錄

1、引語

2、jdk與tomcat的安裝

3、tomcat多實例配置與測試

4、tengine安裝與負載均衡配置

5、整體測試

6、思考和優化tomcat配置流程

7、總結

1、引語

    有沒有這樣一種情況,你在一臺服務器跑了一個tomcat實例,當有一天你發現不管你怎麼優化tomcat,它的併發能力處理能力始終上不去了,而你服務器的硬件資源還有一部份剩餘時,這時你就得采用tomcat啓用多實例的方式,讓剩下的硬件資源也一起利用起來,讓用戶的請求分攤到多個實例上來處理,這樣只要硬件資源允許能大大提升tomcat的整體性能,利用前端的反向代理軟件還可實例tomcat實例級別的高可用和負載均衡。前端的反向代理軟件選擇比較多,可以用LVS、Haproxy、Apache、Nginx,比較常用的是Apache和Nginx。Apache可以實現http和AJP方式與tomcat結合,而Nginx只能選擇http的方式。關於apache與tomcat整合請見http://zhaochj.blog.51cto.com/368705/1641939

    接下來我們在tomcat上實現多實例,並利用nginx以反向代理的方式與tomcat整合實現負載均衡。

2、jdk與tomcat的安裝

    這次測試所使用的系統環境與軟件如下所示:

[root@nod3 tomcat]# cat /etc/issue
CentOS release 6.4 (Final)
Kernel \r on an \m
[root@nod3 tomcat]# uname -r
2.6.32-358.el6.x86_64
[root@nod3 tomcat]# pwd
/root/tomcat
[root@nod3 tomcat]# ls
apache-tomcat-7.0.62.tar.gz  jdk-7u9-linux-x64.rpm  tengine-2.1.0-1.el6.x86_64.rpm

安裝JDK:

[root@nod3 tomcat]# rpm -ivh jdk-7u9-linux-x64.rpm
Preparing...                ########################################### [100%]
   1:jdk                    warning: /etc/init.d/jexec saved as /etc/init.d/jexec.rpmorig    #之前我這系統好像裝過jdk
########################################### [100%]
Unpacking JAR files...
 rt.jar...
Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/rt.pack
 jsse.jar...
Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/jsse.pack
 charsets.jar...
Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/charsets.pack
 tools.jar...
Error: Could not open input file: /usr/java/jdk1.7.0_09/lib/tools.pack
 localedata.jar...
Error: Could not open input file: /usr/java/jdk1.7.0_09/jre/lib/ext/localedata.pack
#上邊的錯誤可忽略,不影響jdk的安裝和使用
[root@nod3 tomcat]# vim /etc/profile.d/java.sh   #導出二進制文件路徑:
export PATH=$PATH:/usr/java/latest/bin
[root@nod3 tomcat]# source /etc/profile.d/java.sh
[root@nod3 tomcat]# java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)
#能夠正常顯示java的版本信息

tomcat安裝:

[root@nod3 tomcat]# tar xf apache-tomcat-7.0.62.tar.gz -C /usr/local/
[root@nod3 local]# ln -sv apache-tomcat-7.0.62 tomcat-7
`tomcat-7' -> `apache-tomcat-7.0.62'
[root@nod3 local]# vim /etc/profile.d/tomcat.sh   #導出tomcat的二進制文件目錄
export PATH=$PATH:/usr/local/tomcat-7/bin
[root@nod3 local]# source /etc/profile.d/tomcat.sh
[root@nod3 local]# catalina.sh version
Using CATALINA_BASE:   /usr/local/tomcat-7
Using CATALINA_HOME:   /usr/local/tomcat-7
Using CATALINA_TMPDIR: /usr/local/tomcat-7/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar
Server version: Apache Tomcat/7.0.62
Server built:   May 7 2015 17:14:55 UTC
Server number:  7.0.62.0
OS Name:        Linux
OS Version:     2.6.32-358.el6.x86_64
Architecture:   x86_64
JVM Version:    1.5.0
JVM Vendor:     Free Software Foundation, Inc.
#上邊正確顯示出tomcat版本信息

注:在以前的博文中寫到在安裝好JDK與tomcat後都全配置一些環境變量,但這裏不需要去配置全局的環境變量,因爲在多實例運行環境下對每個實例的管理都需要用腳本,所在那些環境變量都配置在各自的腳本中,以接下來的內容中有所體現。

3、tomcat多實例配置與測試

    在進行多實例環境部署時先要有個總體的規劃,各個實例存放的目錄在哪裏?你的webapp程序放在哪裏?我這裏是這樣的規劃的,把各個實例都存放在“/usr/local/tomcat_instances/”目錄下,webapp程序就存放在“/usr/local/tomcat_instances/站點名稱/webapps/”目錄下。

 創建實例目錄:

[root@nod3 local]# mkdir -pv /usr/local/tomcat_instances/{nod1.test.com-instance-1,nod1.test.com-instance-2}  
mkdir: created directory `/usr/local/tomcat_instances'
mkdir: created directory `/usr/local/tomcat_instances/nod1.test.com-instance-1'
mkdir: created directory `/usr/local/tomcat_instances/nod1.test.com-instance-2'

拷貝配置文件到各個實例目錄並創建相應的工作目錄:

[root@nod3 local]# cp -r /usr/local/tomcat-7/conf /usr/local/tomcat_instances/nod1.test.com-instance-1/
[root@nod3 local]# cp -r /usr/local/tomcat-7/conf /usr/local/tomcat_instances/nod1.test.com-instance-2/
[root@nod3 local]# mkdir -pv /usr/local/tomcat_instances/nod1.test.com-instance-1/{logs,temp,webapps/webpages,work}
[root@nod3 local]# mkdir -pv /usr/local/tomcat_instances/nod1.test.com-instance-2/{logs,temp,webapps/webpages,work}
[root@nod3 local]# tree /usr/local/tomcat_instances/nod1.test.com-instance-1/
/usr/local/tomcat_instances/nod1.test.com-instance-1/
├── conf
│   ├── catalina.policy
│   ├── catalina.properties
│   ├── context.xml
│   ├── logging.properties
│   ├── server.xml
│   ├── tomcat-users.xml
│   └── web.xml
├── logs
├── temp
├── webapps
│   └── webpages
└── work

6 directories, 7 files
[root@nod3 local]# tree /usr/local/tomcat_instances/nod1.test.com-instance-2/
/usr/local/tomcat_instances/nod1.test.com-instance-2/
├── conf
│   ├── catalina.policy
│   ├── catalina.properties
│   ├── context.xml
│   ├── logging.properties
│   ├── server.xml
│   ├── tomcat-users.xml
│   └── web.xml
├── logs
├── temp
├── webapps
│   └── webpages
└── work

6 directories, 7 files

修改各個實例的監聽端口站點目錄:

第一個實例:

[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-1/conf/server.xml
............
 <!-- 在現在高版本的tomcat中也只是允許本地連接此端口,也是比較安全的 -->
<Server port="8005" shutdown="SHUTDOWN">
...........
<!-- http連接器監聽的端口,第一個實例我不修改此值 -->
<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
..........
<!-- AJP連接器監聽的端口,只有當使用apache作爲反向代理時才支持AJP協議,我這裏採用nginx作爲反應代理,所以禁用 -->
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />  -->
........
<!-- 定義host容器,主要是定義appBase,host name可以不作修改,因爲前端會用nginx作反應代理,tomcat並不直接面向用戶,再增加了<Context />容器 -->
<Host name="localhost"  appBase="/usr/local/tomcat_instances/nod1.test.com-instance-1/webapps"
            unpackWARs="true" autoDeploy="true">
            <Context path="" docBase="webpages" />
.......
<!-- 修改日誌文件的prefix -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="nod1.test.com_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

第二個實例:

[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-2/conf/server.xml
........
<!-- 第2個實例監聽端口更換成8006,只要不與其他實例監聽的端口相沖突即可-->
<Server port="8006" shutdown="SHUTDOWN">
......
<!-- http監聽端口更換爲8081端口,只要不與其他實例中監聽的端口相沖突-->
<Connector port="8081" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
............
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
.........
<Host name="localhost"  appBase="/usr/local/tomcat_instances/nod1.test.com-instance-2/webapps"
            unpackWARs="true" autoDeploy="true">
            <Context path="" docBase="webpages" />
........
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="nod1.test.com_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
.......

爲兩個實例提供測試頁面:

[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-1/webapps/webpages/index.jsp
<html><body><center>
<h1>This is "nod1.test.com-instance-1"</h1>
</br>
Now time is: <%=new java.util.Date()%>
</center>
</body></html>
[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-2/webapps/webpages/index.jsp
<html><body><center>
<h1>This is "nod1.test.com-instance-2"</h1>
</br>
Now time is: <%=new java.util.Date()%>
</center>
</body></html>

編寫腳本啓動各個實例:

[root@nod3 local]# vim /usr/local/tomcat_instances/nod1.test.com-instance-1/tomcat.sh
#!/bin/bash
#Program: manager tomcat instance
#Author: zhaochj
#Date: 2015-6-18
#Version 1.0
#
source /etc/rc.d/init.d/functions
JAVA_HOME=/usr/java/latest
CATALINA_HOME=/usr/local/tomcat-7
CATALINA_BASE=$PWD
export JAVA_HOME CATALINA_HOME CATALINA_BASE
export PATH=$PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
case "$1" in
        start)
                $CATALINA_HOME/bin/startup.sh && exit 0
        ;;
        stop)
                $CATALINA_HOME/bin/shutdown.sh && exit 0
        ;;
        *)
                echo "Usage: $0{start|stop}" && exit 1
        ;;
esac
[root@nod3 local]# chmod +x /usr/local/tomcat_instances/nod1.test.com-instance-1/tomcat.sh
[root@nod3 local]# cd /usr/local/tomcat_instances/nod1.test.com-instance-1/   #切換到實例目錄
[root@nod3 nod1.test.com-instance-1]# sh tomcat.sh start
Using CATALINA_BASE:   /usr/local/tomcat_instances/nod1.test.com-instance-1
Using CATALINA_HOME:   /usr/local/tomcat-7
Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-1/temp
Using JRE_HOME:        /usr/java/latest
Using CLASSPATH:       /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar
Tomcat started.

注意:利用這個腳本來啓動實例時一定要切換到實例的目錄纔可以,因爲腳本中“CATALINA_BASE=$PWD”是把當前目錄當作tomcat的工作目錄,當然也可以寫成絕對的路徑,像這樣“CATALINA_BASE=/usr/local/tomcat_instances/nod1.test.com-instance-1”,這樣定義後使用此腳本就沒有一定要切換到實例的目錄的限制。

[root@nod3 nod1.test.com-instance-1]# ss -tnlp | grep java
LISTEN     0      100                      :::8080                    :::*      users:(("java",2119,42))
LISTEN     0      1          ::ffff:127.0.0.1:8005                    :::*      users:(("java",2119,46))
#兩個端口已正常監聽

再用瀏覽器測試,如下:

wKiom1WD3KrjYzBQAAE6VdAnQ2M672.jpg

[root@nod3 nod1.test.com-instance-1]# cp tomcat.sh ../nod1.test.com-instance-2/  #把腳本文件拷貝到另一個實例
[root@nod3 nod1.test.com-instance-1]# cd ../nod1.test.com-instance-2/
[root@nod3 nod1.test.com-instance-2]# pwd
/usr/local/tomcat_instances/nod1.test.com-instance-2
[root@nod3 nod1.test.com-instance-2]# ls
conf  logs  temp  tomcat.sh  webapps  work
[root@nod3 nod1.test.com-instance-2]# sh tomcat.sh start
Using CATALINA_BASE:   /usr/local/tomcat_instances/nod1.test.com-instance-2
Using CATALINA_HOME:   /usr/local/tomcat-7
Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-2/temp
Using JRE_HOME:        /usr/java/latest
Using CLASSPATH:       /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar
Tomcat started.
[root@nod3 nod1.test.com-instance-2]# ss -tnlp | grep java
LISTEN     0      100                      :::8080                    :::*      users:(("java",2187,42))
LISTEN     0      100                      :::8081                    :::*      users:(("java",2216,42))
LISTEN     0      1          ::ffff:127.0.0.1:8005                    :::*      users:(("java",2187,46))
LISTEN     0      1          ::ffff:127.0.0.1:8006                    :::*      users:(("java",2216,46))
#兩個實例各自監聽的端口都處於LISTEN狀態

打開瀏覽器測試一下第二個實例,如下:

wKiom1WD3OGhptieAADc4XUOwLA943.jpg

4、tengine安裝與負載均衡配置

    tengine是taobao在官方nginx上的基礎上二次發行的一個版本,tengine繼承了nginx的特性,完全兼容nginx,taobao還在nginx基礎上增加了一些新的特性,比如能動態加載模塊等,詳細請見http://tengine.taobao.org/

[root@nod3 tomcat]# pwd
/root/tomcat
[root@nod3 tomcat]# ls
apache-tomcat-7.0.62.tar.gz  jdk-7u9-linux-x64.rpm  tengine-2.1.0-1.el6.x86_64.rpm
[root@nod3 tomcat]# rpm -ivh tengine-2.1.0-1.el6.x86_64.rpm   #安裝tengine
Preparing...                ########################################### [100%]
   1:tengine                ########################################### [100%]
[root@nod3 tomcat]# cp /etc/tengine/nginx.conf{,.back}   #備份原有配置文件
[root@nod3 tomcat]# ls /etc/tengine/nginx.conf*
/etc/tengine/nginx.conf  /etc/tengine/nginx.conf.back  /etc/tengine/nginx.conf.default

修改配置文件,滿足我們的需求:

[root@nod3 tomcat]# vim /etc/tengine/nginx.conf
#user  nobody;
worker_processes  1;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;
events {
    worker_connections  1024;
}
# load modules compiled as Dynamic Shared Object (DSO)
#
#dso {
#    load ngx_http_fastcgi_module.so;
#    load ngx_http_rewrite_module.so;
#}
http {
    include       mime.types;
    default_type  application/octet-stream;
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
    #access_log  logs/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    upstream tomcat_backend {   #定義一個upstream
#       ip_hash;   #調度算法,在測試時可禁用
        server 127.0.0.1:8080;  #定義一個後端tomcat實例
        server 127.0.0.1:8081;  #定義另一個後端tomcat實例
        server 127.0.0.1:9111 backup;  #定義一個backup,當後端的所有tomcat實例down後向這個備份站點調度
        check interval=3000 rise=2 fall=5 timeout=1000 type=http;  #定義對後端服務器的健康檢測機制
        check_http_send "HEAD / HTTP/1.0\r\n\r\n";   #定義健康檢測方法,以http的方式檢測
        check_http_expect_alive http_2xx http_3xx;
    }
    server {   #定義backup的虛擬主機
        listen 9111;
        server_name localhost;
        location / {
            root html;
            index index.html;
        }
    }
    server {  #定義對外提供服務的虛擬主機
        listen       80;
        server_name  nod3.test.com;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location / {
            proxy_pass http://tomcat_backend;    #定義向後端轉發
        }
        location /status {  #定義健康檢測的監控頁面
            auth_basic "nginx check health status";  #定義進行健康頁面的認證方式
            auth_basic_user_file /etc/tengine/.htpasswd;  #認證用戶名及密碼在存放路徑
            check_status;  #開啓健康檢測
            access_log off;  #對後端健康檢測的行爲不記錄到日誌
            allow 192.168.0.0/24;  #允許訪問檢測頁面的網段
            deny all; #拒絕其他訪問
        }
.....略....

修改nginx的備份虛擬主機的主頁面:

[root@nod3 tomcat]# cp /usr/local/tengine/html/index.html{,.back}
[root@nod3 tomcat]# vim /usr/local/tengine/html/index.html
<h1>Sorry, you visit the website is currently under maintenance!</h1>
創建訪問健康檢測頁面的用戶名及密碼:
[root@nod3 tomcat]# htpasswd -c -m /etc/tengine/.htpasswd tengine
New password:
Re-type new password:
Adding password for user tengine
[root@nod3 tomcat]# cat /etc/tengine/.htpasswd
tengine:$apr1$W9U8oBI3$Ae2Q7FpSWGJw9CEcNdqWK0

啓動nginx,測試健康檢測頁面:

[root@nod3 tomcat]# service nginx start
Starting nginx:                                            [  OK  ]
[root@nod3 tomcat]# ss -tnlp | grep nginx
LISTEN     0      128                       *:80                       *:*      users:(("nginx",2360,7),("nginx",2361,7))
LISTEN     0      128                       *:9111                     *:*      users:(("nginx",2360,6),("nginx",2361,6))
#兩個虛擬主機都在監聽狀態

打開瀏覽器訪問健康檢測頁面:

wKiom1WD3Rjz3uYbAAEbvs46jNM675.jpg輸入正確的用戶名和密碼後能正常打開,如下:

wKioL1WD3uzg4ykYAAIIZF00T2A178.jpg

後端的兩個tomcat實例還沒有啓動,所以狀態是down的。

分別切換到兩個tomcat實例目錄,啓動相應的實例:

[root@nod3 tomcat]# cd /usr/local/tomcat_instances/nod1.test.com-instance-1/  #切換到第一個實例目錄
[root@nod3 nod1.test.com-instance-1]# sh tomcat.sh start  #啓動第一個實例
Using CATALINA_BASE:   /usr/local/tomcat_instances/nod1.test.com-instance-1
Using CATALINA_HOME:   /usr/local/tomcat-7
Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-1/temp
Using JRE_HOME:        /usr/java/latest
Using CLASSPATH:       /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar
Tomcat started.
[root@nod3 nod1.test.com-instance-1]# cd ../nod1.test.com-instance-2/  #切換到第二個實例
[root@nod3 nod1.test.com-instance-2]# sh tomcat.sh start  #啓動第二個實例
Using CATALINA_BASE:   /usr/local/tomcat_instances/nod1.test.com-instance-2
Using CATALINA_HOME:   /usr/local/tomcat-7
Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-2/temp
Using JRE_HOME:        /usr/java/latest
Using CLASSPATH:       /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar
Tomcat started.
[root@nod3 nod1.test.com-instance-2]# ss -tnl
State      Recv-Q Send-Q                                   Local Address:Port                                     Peer Address:Port
LISTEN     0      100                                                 :::8080                                               :::*    
LISTEN     0      128                                                  *:80                                                  *:*    
LISTEN     0      100                                                 :::8081                                               :::*    
LISTEN     0      128                                                 :::22                                                 :::*    
LISTEN     0      128                                                  *:22                                                  *:*    
LISTEN     0      128                                                  *:9111                                                *:*    
LISTEN     0      100                                                ::1:25                                                 :::*    
LISTEN     0      100                                          127.0.0.1:25                                                  *:*    
LISTEN     0      1                                     ::ffff:127.0.0.1:8005                                               :::*    
LISTEN     0      1                                     ::ffff:127.0.0.1:8006                                               :::*    
#nginx、tomcat各自監聽的端口已全部處於LISTEN狀態
[root@nod3 nod1.test.com-instance-2]# jps
2389 Bootstrap
2462 Jps
2435 Bootstrap
#兩個tomcat實例已在線

刷新一下健康狀態監控頁面,可見backend server已全部在線,如下圖:

wKioL1WD3xaBuwxPAAHrxdNEkg0787.jpg

5、整體測試

    測試主要有以下幾項,第一:在後端tomcat各實例都正常工作時,看用戶訪問能不能根據調度算法調度到各個實例上;第二:讓後端的一個實例處理維護狀態,測試前端的調度器能否切換到正常工作的實例上;第三:把後端各個實例都處理維護狀態,測試調度器能否把用戶的訪問調度到backup服務器上,返回給用戶一個網站在維護狀態的頁面;    

    先來測試第一項,打開瀏覽器直接訪問前端nginx對外提供服務的地址“nod3.test.com”:

wKiom1WD3YWzGTQoAADo-yNi8D8118.jpg刷新一次後

wKiom1WD3aKgyTQLAAD82C35YnY138.jpg

不斷的刷新頁面,會在後端兩個tomcat實例上輪詢訪問相應的站點,因爲我們在nginx.conf中沒有指定調度算法,默認時就是round-robin算法,第一項測試通過。

    再來測試第二項,先編輯nginx.conf配置文件,讓後端的一個實例處理維護狀態,如下:

[root@nod3 nod1.test.com-instance-2]# vim /etc/tengine/nginx.conf
.....
upstream tomcat_backend {
#       ip_hash;
        server 127.0.0.1:8080 down;  #在server後加上down就表示讓後端的這個實例下線,調度算法不再往這個實例上進行調度
        server 127.0.0.1:8081;
        server 127.0.0.1:9111 backup;
        check interval=3000 rise=2 fall=5 timeout=1000 type=http;
        check_http_send "HEAD / HTTP/1.0\r\n\r\n";
        check_http_expect_alive http_2xx http_3xx;
    }
[root@nod3 nod1.test.com-instance-2]# service nginx reload  #重新載入配置文件
the configuration file /etc/tengine/nginx.conf syntax is ok
configuration file /etc/tengine/nginx.conf test is successful
Reloading nginx:                                           [  OK  ]

刷新健康狀態監控頁面,看有什麼變化,如下圖:

wKiom1WD3cHBYTRfAAHFnvIUAYU141.jpg刷新後,Name爲“127.0.0.1:8080”的這個實例已被踢出,不再是集羣的一部份,這正是我們所要的,再在主頁的訪問頁面上刷新觀察變化,如下圖:

wKiom1WD3fKgl0ZAAADr33YT7KY489.jpg刷新後頁面一直訪問的是tomcat第二個實例提供的頁面內容,所以調度器已把所有的請求都調度到了此實例上。至此,第二項測試通過!

接下來開始第三項測試,也讓第二個實例處理維護狀態,和上邊同樣操作:

[root@nod3 nod1.test.com-instance-2]# vim /etc/tengine/nginx.conf
.....
upstream tomcat_backend {
#       ip_hash;
        server 127.0.0.1:8080 down;  #在server後加上down就表示讓後端的這個實例下線,調度算法不再往這個實例上進行調度
        server 127.0.0.1:8081 down; #同樣加上down,讓實例處理維護狀態
        server 127.0.0.1:9111 backup;
        check interval=3000 rise=2 fall=5 timeout=1000 type=http;
        check_http_send "HEAD / HTTP/1.0\r\n\r\n";
        check_http_expect_alive http_2xx http_3xx;
    }
[root@nod3 nod1.test.com-instance-2]# service nginx reload  #重新載入配置文件
the configuration file /etc/tengine/nginx.conf syntax is ok
configuration file /etc/tengine/nginx.conf test is successful
Reloading nginx:                                           [  OK  ]

再刷新健康狀態監控頁面,如下圖:

wKiom1WD3hrRF3X5AAGXMLncPYU277.jpg上圖中後端的兩個實例都不在了,再看一下主頁訪問頁面,如下圖:

wKioL1WD3-3h0f7ZAAEXghSA0UU014.jpg

刷新後被調度到了nginx自己的虛擬主機上,給用戶提供了一個友好的說明信息。至此,第三項測試也通過!

6、思考和優化tomcat配置流程

    至此,以nginx反向代理多實例tomcat實現負載均衡已能正常的工作,但不得不還要思考以下幾個問題:

第一:安全問題,以上的測試都是基於root用戶來啓動tomcat的各個實例的,以root權限來運行一個服務是不可取的,應該以一個權限較低的用戶來啓動tomcat實例;

[root@nod3 ~]# ps  ax -o user,command | grep java  #下邊的輸出信息可見運行tomcat實例的用戶是“root”
root     /usr/java/latest/bin/java -Djava.util.logging.config.file=/usr/local/tomcat_instances/nod1.test.com-instance-1/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat-7/endorsed -classpath /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat_instances/nod1.test.com-instance-1 -Dcatalina.home=/usr/local/tomcat-7 -Djava.io.tmpdir=/usr/local/tomcat_instances/nod1.test.com-instance-1/temp org.apache.catalina.startup.Bootstrap start
root     /usr/java/latest/bin/java -Djava.util.logging.config.file=/usr/local/tomcat_instances/nod1.test.com-instance-2/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat-7/endorsed -classpath /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat_instances/nod1.test.com-instance-2 -Dcatalina.home=/usr/local/tomcat-7 -Djava.io.tmpdir=/usr/local/tomcat_instances/nod1.test.com-instance-2/temp org.apache.catalina.startup.Bootstrap start

第二:tomcat多實例配置過程比較繁瑣,如果能以一個腳本來完成配置,腳本運行後運維人員只需要提供好應用的源碼,再修改相應的配置文件,這樣一個實例很快就可配置好;

第三:當服務器運行的實例較多時,怎樣對全部實例進行統一的管理,比如我需要所所有的實例都給停掉,再把所有的實例啓動起來,這也需要一個腳本來對所有的實例進行統一的管理;

接下來我們就來解決上邊的三個問題,第一個問題與第二個問題可以統一起來解決,在配置多實例時把手動創建目錄,複製文件,修改權限操作全部在腳本中完成,並在啓動實例時su到一個普通帳號就可實現,腳本如下:

[root@nod3 tomcat]# pwd
/root/tomcat
[root@nod3 tomcat]# ls
apache-tomcat-7.0.62.tar.gz  jdk-7u9-linux-x64.rpm  tengine-2.1.0-1.el6.x86_64.rpm  tomcat_instances_config.sh
[root@nod3 tomcat]# vim tomcat_instances_config.sh
#!/bin/bash
#Program: tomcat multiple instances configuration
#Author: zhaochj
#Date: 2015-6-18
#Version 1.0
#
source /etc/rc.d/init.d/functions
JAVA_HOME=/usr/java/latest
CATALINA_HOME=/usr/local/tomcat-7
INSTANCE_DIR=/usr/local/tomcat_instances
INSTANCE_NAME=nod1.test.com-instance-3
TOMCAT_USER=tomcat
#創建實例目錄
if [ -d $INSTANCE_DIR ];then
    mkdir $INSTANCE_DIR/$INSTANCE_NAME
else
    echo 'Plase check the $INSTANCE_DIR' && exit 1
fi
#複製所需文件及創建相應的工作目錄
if [ -d $INSTANCE_DIR/$INSTANCE_NAME ];then
    cp -r $CATALINA_HOME/conf $INSTANCE_DIR/$INSTANCE_NAME/ > /dev/null 2>&1
    mkdir -pv $INSTANCE_DIR/$INSTANCE_NAME/{logs,temp,webapps/ROOT,work} > /dev/null 2>&1
fi
#創建用於啓動tomcat實例的用戶並修改實例目錄的權限
id $TOMCAT_USER > /dev/null 2>&1 || useradd -r $TOMCAT_USER
chown -R $TOMCAT_USER:$TOMCAT_USER $INSTANCE_DIR/$INSTANCE_NAME
#定義一個控制tomcat啓動關閉的腳本
cat > $INSTANCE_DIR/$INSTANCE_NAME/tomcat.sh << EOF
#!/bin/bash
#Program: manager tomcat instance start or stop
#Author: zhaochj
#Date: 2015-6-18
#Version 1.0
#
source /etc/rc.d/init.d/functions
JAVA_HOME=/usr/java/latest
#定義tomcat啓動參數
JAVA_OPTS="-Xms128M -Xmx128M"
CATALINA_HOME=/usr/local/tomcat-7
CATALINA_BASE=\$PWD
TOMCAT_USER=tomcat
export JAVA_HOME JAVA_OPTS CATALINA_HOME CATALINA_BASE
#定義函數
start() {
        su \$TOMCAT_USER \$CATALINA_HOME/bin/startup.sh
}
stop() {
        su \$TOMCAT_USER \$CATALINA_HOME/bin/shutdown.sh
}
restart() {
        stop
        sleep 3
        start
}
#接受傳遞的參數做相應的操作
case "\$1" in
        start)
             \$1
             ;;
        stop)
             \$1
             ;;
        restart)
             \$1
             ;;
        *)
                echo "Usage: \$0{start|stop|restart}" && exit 1
        ;;
esac
EOF
#修改腳本權限
chmod ug+x $INSTANCE_DIR/$INSTANCE_NAME/tomcat.sh
[root@nod3 tomcat]# chmod +x tomcat_instances_config.sh

接下來測試腳本的可用性:

[root@nod3 tomcat]# ls /usr/local/tomcat_instances/   #存放tomcat各實例目錄裏只有兩個目錄
nod1.test.com-instance-1  nod1.test.com-instance-2
[root@nod3 tomcat]# sh tomcat_instances_config.sh   #運行腳本
[root@nod3 tomcat]# ls /usr/local/tomcat_instances/ -l
total 12
drwxr-xr-x 7 root   root   4096 Jun 18 22:28 nod1.test.com-instance-1
drwxr-xr-x 7 root   root   4096 Jun 18 22:42 nod1.test.com-instance-2
drwxr-xr-x 7 tomcat tomcat 4096 Jun 19 15:15 nod1.test.com-instance-3   #第三個實例目錄生成,並且屬主與屬組已是tomcat
[root@nod3 tomcat]# ls /usr/local/tomcat_instances/nod1.test.com-instance-3/ -l  #只有腳本的屬主、屬組是root,而且其他用戶沒有可執行權限
total 24
drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 conf
drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 logs
drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 temp
-rwxr-xr-- 1 root   root    717 Jun 19 15:15 tomcat.sh
drwxr-xr-x 3 tomcat tomcat 4096 Jun 19 15:15 webapps
drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 15:15 work

提供一個測試主頁做測試:

[root@nod3 tomcat]# vim /usr/local/tomcat_instances/nod1.test.com-instance-3/webapps/ROOT/index.jsp
<html><body><center>
<h1>This is a new tomcat instance!</h1>
</br>
Now time is: <%=new java.util.Date()%>
</center>
</body></html>

啓動第三個實例:

[root@nod3 tomcat]# cd /usr/local/tomcat_instances/nod1.test.com-instance-3/
  #不要忘記切換到實例目錄
[root@nod3 nod1.test.com-instance-3]# ./tomcat.sh start
  #啓動實例
Using CATALINA_BASE:   /usr/local/tomcat_instances/nod1.test.com-instance-3
Using CATALINA_HOME:   /usr/local/tomcat-7
Using CATALINA_TMPDIR: /usr/local/tomcat_instances/nod1.test.com-instance-3/temp
Using JRE_HOME:        /usr/java/latest
Using CLASSPATH:       /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar
Tomcat started.
[root@nod3 nod1.test.com-instance-3]# ss -tnlp | grep java
LISTEN     0      100                      :::8080                    :::*      users:(("java",3592,42))
LISTEN     0      1          ::ffff:127.0.0.1:8005                    :::*      users:(("java",3592,47))
LISTEN     0      100                      :::8009                    :::*      users:(("java",3592,43))
#默認的配置文件監聽的三個端口都處於LISTEN狀態
[root@nod3 nod1.test.com-instance-3]# ps ax -o user,command | grep java
  #查看java進程
tomcat   /usr/java/latest/bin/java -Djava.util.logging.config.file=/usr/local/tomcat_instances/nod1.test.com-instance-3/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xms128M -Xmx128M -Djava.endorsed.dirs=/usr/local/tomcat-7/endorsed -classpath /usr/local/tomcat-7/bin/bootstrap.jar:/usr/local/tomcat-7/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat_instances/nod1.test.com-instance-3 -Dcatalina.home=/usr/local/tomcat-7 -Djava.io.tmpdir=/usr/local/tomcat_instances/nod1.test.com-instance-3/temp org.apache.catalina.startup.Bootstrap start
root     grep java
#上邊的輸出結果可見運行java進程的用戶不再是root,而是tomcat用戶

而用瀏覽器訪問此實例提供的服務也是正常的,見下圖:

wKiom1WD3l6Sh2XGAADa8-rkm8w265.jpg

再測試一下tomcat.sh腳本的stop、restart功能,這裏就不略過。至此用腳本的方式來部署tomcat多實例就順利完成。下邊再來寫一個能管理全部實例的腳本。

腳本內容如下:

[root@nod3 tomcat]# vim tomcat_instances_manager.sh
#!/bin/bash
#Describe: Unified management tomcat instances
#Author: zhaochj
#Date: 2015-6-19
#Version: 1.0
#
source /etc/init.d/functions
JAVA_HOME=/usr/java/latest
CATALINA_HOME=/usr/local/tomcat-7
INSTANCE_DIR=/usr/local/tomcat_instances
TOMCAT_USER=tomcat
SCRIPT_NAME=tomcat.sh
startall() {
    for instance_name in `ls $INSTANCE_DIR`;do
        cd $INSTANCE_DIR/$instance_name
        sh $SCRIPT_NAME start > /dev/null 2>&1 && echo "$instance_name start ok!" || echo "$instance_name unable to start."
        sleep 3
    done
}
stopall() {
    for instance_name in `ls $INSTANCE_DIR`;do
        cd $INSTANCE_DIR/$instance_name
        sh $SCRIPT_NAME stop > /dev/null 2>&1 && echo "$instance_name stoped." || echo "$instance_name unable to stop."
        sleep 3
    done
}
#直接殺掉進程
killall() {
    for jvmpid in `$JAVA_HOME/bin/jps | grep -i bootstrap | awk '{print $1}'`;do
        kill -9 $jvmpid
    done
}
case "$1" in
    startall)
            $1
            ;;
    stopall)
            $1
            ;;
    killall)
            $1
            ;;
    *)
            echo "Usage: $0{startall|stopall|killall}" && exit 1
            ;;
esac
[root@nod3 tomcat]# chmod +x tomcat_instances_manager.sh

爲了方便測試能統一管理全部實例的腳本,先把先前部署的第一個和第二個實例目錄的屬主與屬組修改成tomcat,並把啓動實例的腳本更換爲實例三中的腳本,即如下操作:

[root@nod3 tomcat]# chown -R tomcat:tomcat /usr/local/tomcat_instances/nod1.test.com-instance-1/
[root@nod3 tomcat]# chown -R tomcat:tomcat /usr/local/tomcat_instances/nod1.test.com-instance-2/
[root@nod3 tomcat]# cp /usr/local/tomcat_instances/nod1.test.com-instance-3/tomcat.sh /usr/local/tomcat_instances/nod1.test.com-instance-1/
cp: overwrite `/usr/local/tomcat_instances/nod1.test.com-instance-1/tomcat.sh'? y
[root@nod3 tomcat]# cp /usr/local/tomcat_instances/nod1.test.com-instance-3/tomcat.sh /usr/local/tomcat_instances/nod1.test.com-instance-2/
cp: overwrite `/usr/local/tomcat_instances/nod1.test.com-instance-2/tomcat.sh'? y
[root@nod3 tomcat]# ll /usr/local/tomcat_instances/nod1.test.com-instance-1/
total 24
drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:34 conf
drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 11:02 logs
drwxr-xr-x 2 tomcat tomcat 4096 Jun 18 21:16 temp
-rwxr-xr-x 1 tomcat tomcat  717 Jun 19 16:05 tomcat.sh
drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 21:34 webapps
drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:14 work
[root@nod3 tomcat]# ll /usr/local/tomcat_instances/nod1.test.com-instance-2/
total 24
drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:48 conf
drwxr-xr-x 2 tomcat tomcat 4096 Jun 19 11:03 logs
drwxr-xr-x 2 tomcat tomcat 4096 Jun 18 21:16 temp
-rwxr-xr-x 1 tomcat tomcat  717 Jun 19 16:06 tomcat.sh
drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 21:35 webapps
drwxr-xr-x 3 tomcat tomcat 4096 Jun 18 22:47 work

注意:第三個實例監聽的端口是與第一個實例相沖突的,不要忘記修改,把<Server />容器中的"prot=8005"修改成"prot=8007",註釋掉AJP連接器,http連接器的端口修改爲8082,修改過程不再列出,可以按照上邊修改第二個實例的方法來修改。

用腳本啓動所有的實例:

[root@nod3 tomcat]# sh tomcat_instances_manager.sh startall
nod1.test.com-instance-1 start ok!
nod1.test.com-instance-2 start ok!
nod1.test.com-instance-3 start ok!
[root@nod3 tomcat]# jps
5269 Bootstrap
5241 Bootstrap
5294 Bootstrap
5308 Jps

等一會後查看端口的監聽狀態,如下:

[root@nod3 tomcat]# ss -tnl
State      Recv-Q Send-Q                                   Local Address:Port                                     Peer Address:Port
LISTEN     0      100                                                 :::8080                                               :::*    
LISTEN     0      100                                                 :::8081                                               :::*    
LISTEN     0      100                                                 :::8082                                               :::*    
LISTEN     0      128                                                 :::22                                                 :::*    
LISTEN     0      128                                                  *:22                                                  *:*    
LISTEN     0      100                                                ::1:25                                                 :::*    
LISTEN     0      100                                          127.0.0.1:25                                                  *:*    
LISTEN     0      1                                     ::ffff:127.0.0.1:8005                                               :::*    
LISTEN     0      1                                     ::ffff:127.0.0.1:8006                                               :::*    
LISTEN     0      1                                     ::ffff:127.0.0.1:8007                                               :::* 
#各個實例的監聽端口都是LISTEN狀態

再關閉所有實例,如下:

[root@nod3 tomcat]# sh tomcat_instances_manager.sh stopall
nod1.test.com-instance-1 stoped.
nod1.test.com-instance-2 stoped.
nod1.test.com-instance-3 stoped.
[root@nod3 tomcat]# jps
5439 Jps
#如果stopall不能全部關閉實例,那直接傳遞killall參數。

7、總結

    至此,一個多實例的tomcat配置就完成了,並利用tengine實現了實例級別的負載均衡系統。tomcat的運行也是用root用戶運行,在安全性在得到了一定的保障,在配置多實例時還利用腳本完成配置操作,在對多實例的統一管理上也實現了用腳本來統一管理,這大大節省了部署的時間,提高了生產率,但在生產環境下,在tomcat上線前還得做一些基本安全加固和優化操作,下一次再開一博來嘮嘮。

兩個tomcat多實例的管理腳本請在這裏下載:https://github.com/zhaochj/myrepository/tree/master/shellscripts


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章