叢集應用Session一致性實現的三種方案
1. 為什麼會有Session?
· HTTP 協議本身是無狀態的,這與HTTP協議本來的目的是相符的,客戶端只需要簡單的向伺服器請求下載某些檔案,無論是客戶端還是伺服器都沒有必要紀錄彼此過去的行為,每一次請求之間都是獨立的,好比一個顧客和一個自動售貨機或者一個普通的(非會員制)大賣場之間的關係一樣。
· 由於HTTP協議是無狀態的,而出於種種考慮希望HTTP協議之間的通訊是有狀態的,
· 比如我希望記錄客戶的購買記錄,有利於資料推送
· 登陸狀態的記錄,電影的播放記錄等等
這些都需要記錄相應的狀態,是無狀態變成有狀態,為了記錄狀態出現了cookie和session
1.1. Session簡單介紹
在WEB開發中,伺服器可以為每個使用者瀏覽器建立一個會話物件(session物件),注意:一個瀏覽器獨佔一個session物件(預設情況下)。因此,在需要儲存使用者資料時,伺服器程式可以把使用者資料寫到使用者瀏覽器獨佔的session中,當用戶使用瀏覽器訪問其它程式時,其它程式可以從使用者的session中取出該使用者的資料,為使用者服務。
1.2. Session和Cookie的主要區別
1.2.1. cookie官方解釋:
Cookie,有時也用其複數形式Cookies,指某些網站為了辨別使用者身份、進行session跟蹤而儲存在使用者本地終端上的資料(通常經過加密)。Cookie是由伺服器端生成,傳送給User-Agent(一般是瀏覽器),瀏覽器會將Cookie的key/value儲存到某個目錄下的文字檔案內,下次請求同一網站時就傳送該Cookie給伺服器(前提是瀏覽器設定為啟用cookie)
1.2.2. session官網解釋:
在計算機中,尤其是在網路應用中,稱為“會話控制”。Session物件儲存特定使用者會話所需的屬性及配置資訊。這樣,當用戶在應用程式的 Web 頁之間跳轉時,儲存在 Session 物件中的變數將不會丟失,而是在整個使用者會話中一直存在下去。當用戶請求來自應用程式的 Web 頁時,如果該使用者還沒有會話,則 Web 伺服器將自動建立一個 Session 物件。當會話過期或被放棄後,伺服器將終止該會話。
綜上Session和Cookie的主要區別如下:
A. Cookie是把使用者的資料寫給使用者的瀏覽器。
B. Session技術把使用者的資料寫到使用者獨佔的session中。
C. Session物件由伺服器建立,開發人員可以呼叫request物件的getSession方法得到session物件。
1.3. Session實現原理
伺服器建立session出來後,會把session的id號,以cookie的形式回寫給客戶機,這樣,只要客戶機的瀏覽器不關,再去訪問伺服器時,都會帶著session的id號去,伺服器發現客戶機瀏覽器帶session id過來了,就會使用記憶體中與之對應的session為之服務。
2. 為什麼要保持Session一致性?
首先看標題,應用叢集就說明並非單臺伺服器對外服務,而多個應用伺服器(見下圖)對外提供服務時,使用者的一系列操作可能分散在不同的機器上完成,這時應用端的使用者許可權驗證就會出現問題,因為session資訊沒有同步,許可權驗證會導致使用者操作失敗,所以應用叢集環境下必然要考慮的一個問題就是保證session的一致性。
3. Session一致性問題的三種解決方案
注意:以下3種方案均需要nginx做負載均衡。
3.1. 方案一:Nginx ip_hash
每個使用者請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個後端伺服器,可以解決session的問題。使用ip_hash配置時nginx負載均衡策略使用預設即可(輪詢)。 注意ip_hash配置不能和權重(weight)配置同時使用。
缺點:當伺服器掛掉後,無法對外提供服務。
3.1.1. 配置方式
nginx安裝參照:http://www.runoob.com/linux/nginx-install-setup.html
nginx配置檔案nginx.conf中新增如下配置
1) 叢集配置
upstream sesiontest {
ip_hash;
server 192.168.11.115:8080;
server 192.168.11.120:8080;
}
2) 服務監聽與代理配置
server {
listen 80;
server_name sessiontest;#叢集配置名稱
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
#url:http://ip:埠(預設80可省略)/目錄下的請求揮別代理到叢集的服務其中
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
3.2. 方案二:伺服器Session複製
Tomcat叢集組播共享Session。
缺點:tomcat叢集之間組播通訊消耗系統資源。如果系統遇到網路問題,會導致服務無法正常使用。
3.2.1. 配置方式
1) 修改Tomcat的server.xml配置檔案。在<Engine name="Catalina" defaultHost="localhost">下面新增如下配置
略:
可參考:http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html
2) 修改應用配置檔案web.xml
在應用的web.xml配置檔案中新增<distributable/>標籤
3.3. 方案三:Session集中統一管理
通過Redis或mongodb來存入和讀取Session資訊,叢集應用共享,達到Session統一管理的目標。(spring+redis+spring-session整合)
3.3.1. 配置方式
3.3.1.1. jar包依賴
<!-- Spring-Session+Redis實現session共享依賴 start-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<!-- Spring-Session+Redis實現session共享依賴 end-->
3.3.1.2. 整合配置
spring-redis-session.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--Spring-Session+Redis實現session共享 redis配置-->
<bean id="redisHttpSessionConfiguration"
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="600" />
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="100" />
<property name="maxIdle" value="10" />
</bean>
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
destroy-method="destroy">
<property name="hostName" value="192.168.11.115" />
<property name="port" value="6379" />
<property name="password" value="" />
<property name="timeout" value="3000" />
<property name="usePool" value="true" />
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>
</beans>
3.3.1.3. session管理配置
web.xml配置:必須位於filter鏈的最前面,可在encodingFilter後面
<!-- spring-session:必須位於filter鏈的最前面,可在encodingFilter後面 -->
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.3.1.4. 異常處理
1) 遇到的異常:
Caused by: redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk.
Commands that may modify the data set are disabled. Please check Redis logs for details about the error
2) 解決方法
略
3.3.2. 測試
1) 在Redis的客戶端redis-cli下通過命令keys * 檢視session資訊。
2) 通過hgetall檢視hash資訊