注意:最新版的nginx和nginx_upstream_jvm_route有衝突,不能同時安裝,降級安裝nginx 1.4.7版本的,不安裝1.6.0版本的。javascript
相關文件下載見附件。css
前言
其實搭建集羣環境選擇合適的session共享,服務器一般有兩種管理session的方式:replicated sessions和sticky sessions。
第一種是基於複製的session共享,主要功能是修改tomcat的session存儲機制,使之可以把session序列化存放到memcached中,表明工具是memcached-session-manager。
第二種是實現基於cookie的「粘性會話」(Sticky Session),(咱們須要作的就是調整高度策略,
讓用戶在一次會話週期內的全部請求始終轉發到一臺特定的後端服務器上,這種機制也稱爲粘滯會話(Sticky Sessions),要實現它的關鍵在於如何設計持續性調度算法。參見
http://www.xwuxin.com/?p=1958)這個功能是基於nginx的擴展功能開發的一個擴展插件實現的,主要的表明有nginx_upstream_jvm_route。
關於這點,維基百科上面有相應的描述:
負載均衡 寫道
http://zh.wikipedia.org/zh-cn/負載均衡_(計算機)
持續性
負載均衡器須要處理的一個重要問題是:如何保存用戶會話?若是會話信息保存在後臺服務器,用戶接下來的請求可能會被分配到不一樣的後臺服務器,此時用戶會話就沒法繼續。負載均衡器能夠緩存用戶會話,而後將用戶請求分發到不一樣的後臺服務器。可是這將帶來負載均衡器的負載問題。
一個解決方案是將一個用戶會話中的全部請求都發送到同一個後臺服務器。即persistence或stickiness。這個方法的不足之處在於沒法容錯failover,若是後臺服務器故障,它提供的會話就會沒法取得,任何依賴於它的會話都會丟失。這個問題一般與數據中心有關,儘管Web Service是非連結導向的,可是後端的數據庫先天上仍是連結導向的。
第二個方案是依據用戶名,客戶端IP來分配提供服務的服務器,也能夠隨機分配。由於客戶多是經過DHCP,NAT或者Web代理來鏈接Internet的,其IP地址可能常常變換,這使得這個方案的服務質量沒法保障。隨機分配由負載均衡器將會話信息存儲保存。若是負載均衡器被替換或故障,這些信息可會會丟失;另外(負載均衡器)負載較高的時候,爲保證分配表空間不會被耗盡,超時的分配信息必須被刪除。隨機分配方法也要求客戶會維持會話狀態,若是客戶瀏覽器禁用了cookies的功能,就會引發問題。優秀的負載均衡器會使用多種持續(會話保持)技術,以免其中某些不能夠用時引發故障。
另一個方案是將每一會話信息保存到一個數據庫中。因爲這個方案會增長數據庫的負載,因此這個方案對性能的提升並很差。數據庫最好是用來存儲會話時間比較長的會話數據。爲了不數據庫出現單點故障,而且提升其擴展性,數據庫一般會複製到多臺服務器上,經過負載均衡器來分發請求到數據庫服務器上。微軟ASP.net中的狀態服務器技術就是一個典型的會話數據庫的例子。集羣中的全部服務器都將它們的會話信息保存到狀態服務器上,同時它們能夠向狀態服務器查詢會話數據。
幸運的是有一種更有效的方法,一般客戶瀏覽器能夠保存用戶的每一個會話信息。例如使用瀏覽器cookie,對數據加密並加上一個時間戳就能夠保證安全了;URL重寫。將會話信息存儲在客戶端一般是首選方案,由於這樣負載均衡器就能夠靈活的選擇後臺服務器來處理用戶請求。然而這種方法不適應於一些較複雜的電子商務,由於電子商務中會話數據較大,並且須要服務器須要常常從新處理會話信息;與此同時URL重寫因爲用戶能夠改變會話流數據而存在安全問題;加密客戶端cookies也一直存在着安全方面的爭議,除非全部的會話都經過HTTPS,可是HTTPS很容易遭到中間人攻擊。
持續性
負載均衡器須要處理的一個重要問題是:如何保存用戶會話?若是會話信息保存在後臺服務器,用戶接下來的請求可能會被分配到不一樣的後臺服務器,此時用戶會話就沒法繼續。負載均衡器能夠緩存用戶會話,而後將用戶請求分發到不一樣的後臺服務器。可是這將帶來負載均衡器的負載問題。
一個解決方案是將一個用戶會話中的全部請求都發送到同一個後臺服務器。即persistence或stickiness。這個方法的不足之處在於沒法容錯failover,若是後臺服務器故障,它提供的會話就會沒法取得,任何依賴於它的會話都會丟失。這個問題一般與數據中心有關,儘管Web Service是非連結導向的,可是後端的數據庫先天上仍是連結導向的。
第二個方案是依據用戶名,客戶端IP來分配提供服務的服務器,也能夠隨機分配。由於客戶多是經過DHCP,NAT或者Web代理來鏈接Internet的,其IP地址可能常常變換,這使得這個方案的服務質量沒法保障。隨機分配由負載均衡器將會話信息存儲保存。若是負載均衡器被替換或故障,這些信息可會會丟失;另外(負載均衡器)負載較高的時候,爲保證分配表空間不會被耗盡,超時的分配信息必須被刪除。隨機分配方法也要求客戶會維持會話狀態,若是客戶瀏覽器禁用了cookies的功能,就會引發問題。優秀的負載均衡器會使用多種持續(會話保持)技術,以免其中某些不能夠用時引發故障。
另一個方案是將每一會話信息保存到一個數據庫中。因爲這個方案會增長數據庫的負載,因此這個方案對性能的提升並很差。數據庫最好是用來存儲會話時間比較長的會話數據。爲了不數據庫出現單點故障,而且提升其擴展性,數據庫一般會複製到多臺服務器上,經過負載均衡器來分發請求到數據庫服務器上。微軟ASP.net中的狀態服務器技術就是一個典型的會話數據庫的例子。集羣中的全部服務器都將它們的會話信息保存到狀態服務器上,同時它們能夠向狀態服務器查詢會話數據。
幸運的是有一種更有效的方法,一般客戶瀏覽器能夠保存用戶的每一個會話信息。例如使用瀏覽器cookie,對數據加密並加上一個時間戳就能夠保證安全了;URL重寫。將會話信息存儲在客戶端一般是首選方案,由於這樣負載均衡器就能夠靈活的選擇後臺服務器來處理用戶請求。然而這種方法不適應於一些較複雜的電子商務,由於電子商務中會話數據較大,並且須要服務器須要常常從新處理會話信息;與此同時URL重寫因爲用戶能夠改變會話流數據而存在安全問題;加密客戶端cookies也一直存在着安全方面的爭議,除非全部的會話都經過HTTPS,可是HTTPS很容易遭到中間人攻擊。
這裏先介紹第二種實現方式。html
在服務機器上建立一個目錄/home/oracle/http,全部的文件都放在該文件下:
準備工做java
進入到
http://nginx.org/en/download.html,不要下載nginx最新的穩定版本:nginx-1.6.0.tar.gz,下載nginx-1.4.7,下載地址是
http://nginx.org/download/nginx-1.4.7.tar.gz
解壓到服務器上,進入到/home/oracle/http/nginx-1.4.7目錄下,這裏採用的是源代碼編譯的方式進行安裝,參考文檔說明,相關的configure設置爲:
一個參考的configure示例:
./configure
--sbin-path=/usr/local/nginx/nginx
--conf-path=/usr/local/nginx/nginx.conf
--pid-path=/usr/local/nginx/nginx.pid
--with-http_ssl_module
--with-pcre=../pcre-4.4
--with-zlib=../zlib-1.1.3
全部的代碼寫到一行。
安裝nginx須要安裝相應的依賴包,依賴包一共有三個
pcre-8.3五、zlib-1.2.8和nginx-upstream-jvm-route-0.1
分別到
http://sourceforge.net/projects/pcre/、
http://zlib.net/以及
https://code.google.com/p/nginx-upstream-jvm-route/官網上去下載最新版本便可。
下載完成以後解壓,其中nginx-1.4.7解壓到/home/oracle/http/nginx-1.4.7,pcre-8.35解壓到/home/oracle/http/pcre-8.35,zlib-1.2.8解壓到/home/oracle/http/zlib-1.2.8,nginx_upstream_jvm_route解壓到/home/oracle/http/nginx_upstream_jvm_route。
其中nginx-upstream-jvm-route-0.1.tar.gz包裏面的文件是舊的,會致使nginx安裝不成功,須要使用nginx_upstream_jvm_route.tar.gz裏面的文件替換掉解壓出的文件,或者去https://code.google.com/p/nginx-upstream-jvm-route/網站上checkout最新的源代碼。
一、準備工做作好以後,進入到/home/oracle/http/nginx-1.4.7目錄下,爲nginx添加nginx_upstream_jvm_route的模塊,運行下面的命令:
patch -p0 < /home/oracle/http/nginx_upstream_jvm_route/jvm_route.patch
若是使用以前的舊的jar包的話會致使patch失敗:node
若是失敗的話就須要把以前的/home/oracle/http/nginx-1.4.7下面的文件從新解壓一份從新patch:nginx
此時就能patch成功,能夠進行下面的安裝。web
二、patch完成以後就能夠安裝,這裏configure使用下面的命令:
./configure --prefix=/home/oracle/http/nginxserverfiles #nginx安裝的根路徑 --sbin-path=/home/oracle/http/nginxserverfiles/nginx #nginx的bin路經 --conf-path=/home/oracle/http/nginxserverfiles/nginx.conf #nginx的配置文件路徑 --pid-path=/home/oracle/http/nginxserverfiles/nginx.pid #nginx運行時生成pid的文件路徑 --with-http_ssl_module #編譯ssl模塊 --with-pcre=../pcre-8.35 #pcre路徑,pcre主要是爲ngx_http_rewrite_module模塊提供正則表達式的支持的 --with-zlib=../zlib-1.2.8 #zlib路徑,zlib是ngx_http_gzip_module模塊所必需的 --add-module=/home/oracle/http/nginx_upstream_jvm_route #增長一個外部模塊,爲tomcat集羣的session共享提供解決方案
configure完成以後就是make和make install。完成以後就會在/home/oracle/http/nginxserverfiles目錄下面生成相應的文件:正則表達式
三、接下來安裝tomcat集羣,這裏爲了演示方便,採用單機多端口的方式進行集羣搭建,將一個tomcat包分別解壓到/home/oracle/http/
apache-tomcat-a、/home/oracle/http/
apache-tomcat-b、/home/oracle/http/
apache-tomcat-c目錄下。
四、首先是/home/oracle/http/nginxserverfiles/nginx.conf配置文件的修改:
#運行NGINX所使用的用戶和組 user root; #nginx進程數,建議按照cpu數目來指定,通常爲它的倍數,每一個進程消耗約10M內存 worker_processes 5; #日誌信息 PID文件 error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; pid logs/nginx.pid; #工做模式及鏈接數上限 events { #使用epoll的I/O模型 use epoll; #該值受系統進程最大打開文件數限制,須要使用命令ulimit -n 查看當前設置 worker_connections 300000; } #設定http服務器,利用它的反向代理功能提供負載均衡支持 http { #這裏是您須要修改的地方,修改成您的服務器IP:端口號 srun_id爲您在tomcat中所配置的jvmRoute upstream localhost.com{ server localhost.com:18080 srun_id=a; server localhost.com:28080 srun_id=b; server localhost.com:38080 srun_id=c; jvm_route $cookie_JSESSIONID|sessionid reverse; } include mime.types; #設置默認類型是二進制流,若未設置時,好比未加載PHP時,是不予解析,用瀏覽器訪問則出現下載窗口 default_type application/octet-stream; charset UTF-8; #設定請求緩衝 server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 8m; limit_rate 1024k; #access_log logs/access.log main; sendfile on; tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; tcp_nodelay on; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_buffer_size 64k; fastcgi_buffers 4 64k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 128k; gzip on; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; server { listen 80; server_name localhost.com; #全部的代理資源存放到存放路徑 root /home/oracle/http/www; index index.html index.htm index.jsp; error_page 404 /404.html; charset UTF-8; #access_log logs/host.access.log main; error_page 500 502 503 504 /50x.html; location / { #root html; proxy_pass http://localhost.com; proxy_redirect off; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ .*\.(js|css)?$ { expires 1h; } location /Nginxstatus { #stub_status on; access_log off; } } log_format access '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $http_x_forwarded_for'; }
而後是tomcat的server.xml文件修改,進入到/home/oracle/http/apache-tomcat-a/conf路徑,修改server.xml文件:算法
<?xml version='1.0' encoding='utf-8'?> <Server port="18081" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <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> <Service name="Catalina"> <Connector port="18080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="18443" /> <Connector port="18009" protocol="AJP/1.3" redirectPort="18443" /> <Engine name="Catalina" defaultHost="localhost.com" jvmRoute="a"> <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> </Cluster> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost.com" appBase="/home/oracle/http/www" 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" /> </Host> </Engine> </Service> </Server>
注意上述文件中的紅色字體和紅色加粗字體的描述部分,而後編輯web.xml文件,而後在後面添加上下面的內容:數據庫
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <distributable/>
而後從tomcat/webapps下面的ROOT文件夾拷貝到/home/oracle/http/www文件夾下。
用下面的內容替換掉index.jsp的內容:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> session is : <% HttpSession sess = request.getSession(); sess.setMaxInactiveInterval(100); out.print(sess); %> <br> cookie is : <% out.print(request.getHeader("Cookie")); %> </body> </html>
而後就能夠啓動三個tomcat和nginx了,在運行過程當中只要保證至少有一個tomcat實例在運行就能夠保證session不會丟失,中途關閉重啓多臺tomcat沒有影響。