session共享方式

第一種方式:nginx配置 ip_hash輪詢機制實現,這個實現方式簡單,但是有很大的侷限性,同一個ip,會被分配始終訪問同一個tomcat,因爲ip不變,nginx計算出來的hash也就不變,除非這個tomcat掛了,纔會分配到另外一個tomcat訪問,萬一出現這種情況,session就會丟失,分配到的新tomcat並沒有它在前一個tomcat裏的session,所有,這並不是session共享,只是儘量保證某一連接始終分發到固定的服務器而已。
假如在一個公司內的所有員工,訪問的外網服務器,通過這種方式配置集羣,將無法實現負載均衡,因爲對外網nginx而言,這些人全部來自一個外網ip,所以,全部會被分配到一個tomcat上,而集羣中其它tomcat將一直閒置。
另外,也有可能出現某些高頻訪問,始終壓在一臺服務器上的情況。
這樣都是大大的弱化了nginx的負載均衡能力。

第二種方式,nginx不配置,tomcat服務配置session複製功能。
在Server.xml中,1)取消Cluster節點的註釋. (2)保持每個Engine 節點jvmRoute的值是相同的.
tomcat的session複製是基於IP組播(multicast)來完成的。將集羣的tomcat通過配置統一的組播IP和端口來確定一個集羣組, 當一個node的session發生變更的時候, 它會向IP組播發送變更的數據, IP組播會將數據分發給所有組裏的其他成員(node).
(網絡通訊方式,1,tcp/ip,一對一通訊。2,udp廣播方式通訊,發送全部網內機器某一端口,需要的機器監聽端口接受數據處理數據,不需要的機器直接不理睬就行。3,組播方式,配置統一的組播IP和端口來確定一個集羣組,集羣內開黑通訊)

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">

<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>

<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
bind="127.0.0.1"
address="228.0.0.4"<!--保留ip,用於組播-->
port="45564"
frequency="500"
dropTime="3000"/> 
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4001"<!--如果是在同一臺機器上的兩個tomcat做負載,則此端口則不能重複-->
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

<!--這是發佈相關,不要,可能會報錯-->
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>

<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
同時, 在web.xml裏增加<distributable/>描述,就是告訴tomcat,這個項目是可分佈式的。。。

這種配置方式,相對簡單,無需更改項目代碼,也實現了真正的session共享,整個集羣,只要有一個tomcat存活,那麼其它tomcat都死掉了都重啓了,session都不會丟失。
缺點也很明顯,session在網絡內複製的過程中,會有一定的延遲,當然,延遲長短,取決於網絡環境、session個數(用戶多不多)、session信息大小、集羣的服務器個數。

所以,它適用於小集羣、少用戶的web服務。。。

以上配置會報錯
ava.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: xxxxxxxx
原因是:tomcat停止時,保存session資源,然後在重啓服務後,會嘗試恢復session。
解決辦法一:配置tomcat在關閉的時候就不去保存session資源。
配置於Context標籤內

PersistentManager對Session進行持久化到文件系統或數據庫中

 <Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="false">
    <Store className="org.apache.catalina.session.FileStore"/>
</Manager>
1
2
3
解決辦法二:
將那些需要放在session的類進行序列化。
也就是讓類實現接口java.io.Serializable即可。

第三種方式,nginx不配置,使用nosql或者sql數據庫,實現session共享,特別是spring + redis方式。

1、添加依賴
特別注意,引入不同的版本時,可能會引起原有jar包版本不兼容

<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>
1
2
3
4
5
6
7
8
9
10
2、配置
spring-mvc.xml:
RedisHttpSessionConfiguration

<bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
    <property name="maxInactiveIntervalInSeconds" value="600"/>
</bean>
1
2
3
由於使用了這裏的配置,由redis負責接管Session,原來web.xml裏配置的Session超時時間就會失效了

還有一個redis連接池的配置。。。。
redis.clients.jedis.JedisPoolConfig
org.springframework.data.redis.connection.jedis.JedisConnectionFactory

3、web.xml添加攔截器
特別注意,這個filter要寫在比較靠前的第一個的位置

<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>
1
2
3
4
5
6
7
8
這種配置方式,也較簡單,需要更改一定的項目配置,實現了真正的session共享,可靠性很高。
缺點是項目配置有一定修改,每次訪問多一個網絡開銷(去redis取session,開銷也很小了)

所以,它適用於大集羣、多用戶的web服務。。。

Spring4.3,這裏需要注意,所有Spring的xml的配置文件裏標籤要變爲 -4.3.xsd

靜態文件資源訪問配置:要注意這裏的靜態文件不要放到WEB-INF裏,要放到項目內,例如項目下的resources
<mvc:resources mapping="/**" location="/resources/" />這樣就可以放行靜態文件的訪問

還有一個比較大的坑,使用Spring Session實際是Spring加入了一個Filter,其本質是:對每一個請求的request都會被DelegatingFilterProxy進行了一次封裝。那麼,在Controller裏面拿出的request實際上是封裝後的request,因爲Session要存在Session,所以調用request.getSession()的時候,實際上拿到是Spring封裝後的session ,因此對於request實際上拿到是Spring封裝後的request。那麼可能會導致Request.getInputStream無法獲取到流數據,對於使用raw格式,即非Key,Value參數的提交 會被Spring-Session獲取,當contentType爲application/x-www-form-urlencoded時,就會被解析成Paramater,從而導致request.getInputStream的數據再獲取時爲空了。

還有就是Controller裏如果用到了request.setCharacterEncoding(“GBK”); 設置字符集,這樣的請求也無法生效,因爲request.setCharacterEncoding只有在request.getSession或者request.getParamater之前使用有效,而由於使用Spring Session加入了filter,所以Controller裏的request.setCharacterEncoding這種方式轉編碼就失效了,解決辦法,可以是new String(request.getParameter(key).getBytes(), “GBK”),獲取到參數後轉碼。

另外,對於多項目間session共享,跨域名session共享,還得繼續分析源碼,坑較多。
原文:https://blog.csdn.net/ldms1980/article/details/82746128 
 

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