本文講解我們如何使用Nginx做反向帶服務器,實現nginx與tomcat服務器集群做負載均衡。
1、在/usr/local/ngnix/conf 創建文件 nginx-tomcat.conf
文件內容:
user nobody;
worker_processes 2;
events {
worker_connections 1024;
}
http{
# upstream 配置一組後端服務器,
# 請求轉發到upstream後,nginx按策略將請求指派出某一服務器
# 即配置用於負載均衡的服務器群信息
upstream tomcats{
fair;
server 121.42.41.143:8080;
server 219.133.55.36;
}
server {
listen 80;
server_name 121.42.41.143;
access_log logs/tomcat-nginx.access.log combined;
# 反向代理設置,將所有/路徑下請求發給本機上的tomcat
location / {
#root html;
index index.html index.htm;
#==========Nginx提供的代理============
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://tomcats;
}
}
}
2、使用該配置文件啟動nginx (啟動前先關閉nginx)
[root@iZ28b4kreuaZ bin]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-tomcat.conf

worker_processes 2;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# upstream 配置一組後端服務器,
# 請求轉發到upstream後,nginx按策略將請求指派出某一服務器
# 即配置用於負載均衡的服務器群信息
upstream backends {
#=========均衡策略=============
#none 輪詢(權重由weight決定)
#ip_hash 通過hash算法將用戶的請求與第一次請求的服務器進行綁定,後續該用戶所有的請求都將被分配到該服務器上。除非該服務器掛掉。
#============== 第三方 均衡策略===========
#fair 根據各個服務器的性能的不同,自動選擇使用響應能力強的服務器。
#url_hash 根據url選擇服務器。
#===============服務器集==============
server 192.168.1.62:8080;
server 192.168.1.63;
#==========weight權重策略:權重值越高負載越大==========
# server 192.168.1.64 weight=5;
#===============backup:備份機,只有非備份機都掛掉了才啟用===============
server 192.168.1.64 backup;
#==============down: 停機標志,不會被訪問(對臨時維護的服務器設置)=============
server 192.168.1.65 down;
# max_fails:達到指定次數認為服務器掛掉;
# fail_timeout:掛掉之後過多久再去測試是否已恢復
server 192.168.1.66 max_fails=2 fail_timeout=60s;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
# 反向代理設置,將所有/proxy_test/路徑下請求發給本機上的tomcat
location /proxy_test/ {
proxy_pass http://localhost:8080;
}
# 負載均衡設置,將所有jsp請求發送到upstream backends指定的服務器群上
location ~ \.jsp$ {
proxy_pass http://backends;
# 真實的客戶端IP
proxy_set_header X-Real-IP $remote_addr;
# 請求頭中Host信息
proxy_set_header Host $host;
# 代理路由信息,此處取IP有安全隱患
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 真實的用戶訪問協議
proxy_set_header X-Forwarded-Proto $scheme;
# 默認值default,
# 後端response 302時 tomcat header中location的host是http://192.168.1.62:8080
# 因為tomcat收到的請求是nginx發過去的, nginx發起的請求url host是http://192.168.1.62:8080
# 設置為default後,nginx自動把響應頭中location host部分替換成當前用戶請求的host部分
# 網上很多教程將此值設置成 off,禁用了替換,
# 這樣用戶浏覽器收到302後跳到http://192.168.1.62:8080,直接將後端服務器暴露給浏覽器
# 所以除非特殊需要,不要設置這種畫蛇添足的配置
proxy_redirect default;
}
# 一個url重寫的例子,浏覽器請求 /page.go時,url被重寫成/test/page.jsp
location ~ \.go$ {
rewrite ^(.*)\.go$ /test/$1\.jsp last;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
View Code
fair策略:根據各個服務器的性能的不同,自動選擇使用響應能力強的服務器。該策略是第三方提供的,所以要先安裝。
安裝步驟
1、下載 gnosek-nginx-upstream-fair-a18b409.tar.gz
2、解壓 tar zxvf gnosek-nginx-upstream-fair-a18b409.tar.gz
3、將解壓後的文件移動到 /usr/local目錄下並 改名為 nginx-upstream-fair

4、將該模塊添加到我們安裝的nginx中
a、首先進入nginx-1.8.1源文件目錄下在執行:
[root@iZ28b4kreuaZ nginx-1.8.1]# ./configure --prefix=/usr/local/nginx --add-module=/usr/local/nginx-upstream-fair/
b、執行:make 進行編譯
c、進入 nginx-1.8.1/objs/下將最新的nginx啟動項 覆蓋原來的 /usr/local/nginx/sbin/nginx啟動項。
[root@iZ28b4kreuaZ objs]# cp nginx /usr/local/nginx/sbin
d、開啟Nginx 看看是可以使用
問題:當我們的用戶在tomcat1服務器上登錄後,tomcat1會保存用戶的登錄信息,但當用戶的請求被代理服務器分配給tomcat2/tomcat3服務器時,這時就會出現tomcat2/tomcat3無法獲取用戶登錄信息,從而導致用戶需要重新登錄的現象。我們有三種解決方案:
1、同一個用戶的請求鎖定在同一台服務器上,這樣就不會存在session在不同服務器之間共享問題。這種方案簡單,但缺乏容錯性(一旦服務器故障,那用戶的請求將被分配給其他服務器,這時就需要重新登錄)
實現方式:設置集群策略為 ip_hash ;
upstream tomcats{
ip_hash;
}
2、session復制方式: 當任何服務器中session值發生改變,他都會將該改變廣播給其他服務器,當其他服務器收到廣播後也做相應的改變,從而實現session在所有服務器中一直。缺點 當集群中tomcat服務器很多時會增加網絡負荷,性能低下。實現方式:
a、在tomcat的server.xml中配置session廣播
<!-- 基於網絡廣播的策略,一個節點session變化,其它節點同步復制,節點多或數據量大時性能低下 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="192.168.6.223"
port="8080"/>
</Channel>
</Cluster>
b、在我們的分布式應用的web.xml 中添加 <distributable/>標簽
<distributable/>:作用是公告我們的應用可以處於集群環境中。
3、通過創建額外的共享空間用來管理session,一般我們使用分布式緩存技術redis、memcached緩存技術,在這裡我麼使用memcached。
a、memcached的安裝:http://www.cnblogs.com/jalja/p/6121978.html
b、memcached 的 session共享原理
粘性共享:

非粘性:

c、tomcat訪問memcached的相關環境(我們使用的是tomcat7)
1. 復制jar包到tomcat/lib目錄,jar分三類
1)spymemcached.jar memcached java客戶端
2)memcached相關的包 memcached-session-manager-{version}.jar 核心包 memcached-session-manager-tc{tomcat-version}-{version}.jar Tomcat版本相關的包
3)序列化工具包,有多種可選方案,不設置時使用jdk自帶序列化,其它可選kryo,javolution,xstream,flexjson等 msm-{tools}-serializer-{version}.jar 其它序列化工具相關包 一般第三方序列化工具不需要實現serializable接口

d、配置Context,加入處理session的Manager MemcachedBackupSessionManager
Context配置查找順序:
1)conf/context.xml 全局配置,作用於所有應用
2) conf/[enginename]/[hostname]/context.xml.default 全局配置,作用於指定host下全部應用
3) conf/[enginename]/[hostname]/[contextpath].xml 只作用於contextpath指定的應用
4) 應用META-INF/context.xml 只作用於本應用
5) conf/server.xml <Host>下 作用於Context docBase指定的應用
如果只希望session管理作用於特定應用,最好用3,4方式設置,希望作用全體,可用1,2,5設置
conf/context.xml的配置:

1 <?xml version="1.0" encoding="UTF-8"?>
2
3 <Context>
4
5 <WatchedResource>WEB-INF/web.xml</WatchedResource>
6 <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
7
8 <!-- sticky session 最小配置-->
9 <!-- className 管理器類名 -->
10 <!-- memcachedNodes memcached服務器節點,以節點名:主機:端口形式表示,其中節點名隨意命名,但不同tomcat間要一致 -->
11 <!-- sticky隱含默認值為true,此時為sticky session模式 -->
12 <!-- failoverNodes 僅適用於sticky模式, n1表示主要將session備份到n2,如果n2不可用,再用n1-->
13 <!-- 另一台服務器配置正好相反,這樣保證將session保存到其它機器,避免整個機器崩潰時tomcat與session一起崩潰-->
14 <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
15 memcachedNodes="n1:192.168.1.62:11211,n2:192.168.1.63:11211"
16 failoverNodes="n1"
17 />
18
19 <!-- 經常用到的生產環境sticky(粘性)模式配置 -->
20 <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
21 memcachedNodes="n1:192.168.1.62:11211,n2:192.168.1.63:11211"
22 failoverNodes="n1"
23 requestUriIgnorePattern=".*\.(jpg|png|css|js)$"
24 memcachedProtocol="binary"
25 transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
26 />
27
28 <!-- 經常用到的生產環境non-sticky(非粘性模式)模式配置 -->
29 <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
30 memcachedNodes="n1:192.168.1.62:11211,n2:192.168.1.63:11211"
31 sticky="false"
32 sessionBackupAsync="false"
33 lockingMode="auto"
34 requestUriIgnorePattern=".*\.(jpg|png|css|js)$"
35 memcachedProtocol="binary"
36 transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
37 />
38
39 <!--
40 <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
41 memcachedNodes="n1:192.168.1.62:11211,n2:192.168.1.63:11211"
42
43 #sticky模式,默認true
44 sticky="false"
45
46 #僅適用於sticky模式,n1表示主要將session備份到n2,如果n2不可用,再用n1
47 failoverNodes="n1"
48
49 #忽略的請求類型,這些類型請求不處理session
50 requestUriIgnorePattern=".*\.(jpg|png|css|js)$"
51
52 #例如context中設置sessionPath=/時,一個host下多個應用可能有相同session_id,
53 #此時向memcached寫入時會造成混亂,可通過以下方式加前綴區分不同應用
54 storageKeyPrefix="static:name|context|host|webappVersion|context.hash|host.hash|多項組合,以,間隔"
55
56 #設置mecached協議數據傳輸方式,默認text,設為binary有助力性能提升
57 memcachedProtocol="binary"
58
59 #是否異步存儲session變化,默認true,性能好,適用於sticky模式,
60 #non-sticky時建議設置為false,避免延遲造成信息不一致
61 sessionBackupAsync="false"
62
63 #僅適用於non-sticky模式,為避免同步編輯沖突,在修改session時鎖定
64 #同步編輯一種可能發生的情況是在ajax請求時,同一頁多個請求同時發起,可能會訪問不同後端
65 #auto 讀模式不鎖寫模式鎖
66 #uriPattern模式,將URI+"?"+queryString與模式Regex匹配,如果匹配則鎖定
67 lockingMode="none|all|auto|uriPattern:Regex"
68
69 #使用第三方序列化工具,提高序列化性能
70 #常用的第三方工具kryo, javolution, xstream等
71 #此時需要向tomcat/lib下添加相關jar包
72 transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
73
74 />
75 -->
76
77 </Context>
View Code
四、集群環境開發注意事項
1、實體類要序列化( implements Serializable)
private static final long serialVersionUID = 3349238980725146825L;
2、獲取客戶端請求地址的方式 。在nginx-tomcat.conf中添加如下配置:
server {
location / {
proxy_set_header X-Real-IP $remote_addr; # 真實的客戶端IP
}
}
java代碼:
public static String getIp(HttpServletRequest request){
String remoteIp =request.getRemoteAddr();
String headIp=request.getHeader("X-Real-IP");
return headIp==null?remoteIp:headIp;
}
3、動靜分離
把靜態文件放在nginx服務器中(css、js、圖片)