標籤:data red tip 會話 sys 虛擬機器 logs current 應用
前言
對於分布式使用Nginx+Tomcat實現負載平衡,最常用的均衡演算法有IP_Hash、輪訓、根據權重、隨機等。不管對於哪一種負載平衡演算法,由於Nginx對不同的請求分發到某一個Tomcat,Tomcat在啟動並執行時候分別是不同的容器裡,因此會出現session不同步或者丟失的問題。
文末分享了我一部分私人收藏 有興趣的可以收藏看一下的 都是架構師進階的內容
實際上實現Session共用的方案很多,其中一種常用的就是使用Tomcat、Jetty等伺服器提供的Session共用功能,將Session的內容統一儲存在一個資料庫(如MySQL)或緩衝(如Redis)中。 如何使用 tomcat-redis-session-manager 開源項目解決分布式session跨域的問題,他的主要思想是利用Servlet容器提供的外掛程式功能,自訂HttpSession的建立和管理原則,並通過配置的方式替換掉預設的策略。tomcat-redis-session-manager重寫了Tomcat的org.apache.catalina.session.ManagerBase裡邊的具體寫的操作, 將tomcat的session儲存位置指向了Redis:
https://img-blog.csdn.net/20170226114651267?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxnZW4xNTczODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
RedisSessionManager繼承了org.apache.catalina.session.ManagerBase並重寫了add、findSession、createEmptySession、remove等方法,並將對session的增刪改查操作指向了對Redis資料存放區的操作。
有興趣可參考一篇Tomcat中session的管理機制:http://www.cnblogs.com/interdrp/p/4935614.html
不過使用過tomcat-redis-session-manager 的都應該知道,配置相對還是有一點繁瑣的,需要人為的去修改Tomcat的配置,需要耦合Tomcat等Servlet容器的代碼,並且對於分布式Redis叢集的管理並不是很好,與之相對的個人認為比較好的一個架構Spring Session可以真正對使用者透明的去管理分布式Session。
Spring Session不依賴於Servlet容器,而是Web應用代碼層面的實現,直接在已有項目基礎上加入spring Session架構來實現Session統一儲存在Redis中。如果你的Web應用是基於Spring架構開發的,只需要對現有項目進行少量配置,即可將一個單機版的Web應用改為一個分布式應用,由於不基於Servlet容器,所以可以隨意將項目移植到其他容器。
Spring Session使用
官方地址:http://projects.spring.io/spring-session/
官方文檔地址:http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/
Spring Session提供了一套建立和管理Servlet HttpSession的方案。Spring Session提供了叢集Session(Clustered Sessions)功能,預設採用外置的Redis來儲存Session資料,以此來解決Session共用的問題。
一、特性
Spring Session提供以下特性:
API和用於系統管理使用者會話的實現;
HttpSession - 允許以應用程式容器(即Tomcat)中性的方式替換HttpSession;
Clustered Sessions - Spring Session讓支援叢集會話變得不那麼繁瑣,並且不和應用程式容器金習性綁定到。
Multiple Browser Sessions - Spring會話支援在單個瀏覽器執行個體中管理多個使用者的會話。
RESTful APIs - Spring Session允許在headers 中提供會話ID以使用RESTful API。
二、基於XML配置方式的Spring Session案例實現
基於SSM架構的一個小案例,Git OS項目代碼地址:http://git.oschina.net/xuliugen/spring-session-demo
https://img-blog.csdn.net/20170226141717129?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxnZW4xNTczODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
項目展示:
https://img-blog.csdn.net/20170226142056838?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxnZW4xNTczODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
(1)基本環境需求
進行使用Spring Session的話,首先的是已經安裝好的有一個 Redis伺服器!
(2)添加項目依賴(最基本的依賴使用)
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.3.0.RELEASE</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>3.5.0.Final</version>
</dependency>
(3)添加Spring設定檔
添加了必要的依賴之後,我們需要建立相應的Spring配置。Spring配置是要建立一個Servlet過濾器,它用Spring Session支援的HttpSession實現來替換容器本身HttpSession實現。這一步也是Spring Session的核心。
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
上述代碼注釋:
https://img-blog.csdn.net/20170226145153620?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxnZW4xNTczODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
LettuceConnectionFactory執行個體是配置Redis的ConnectionFactory。
注意:
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
查看原始碼可以看到,預設的Redis連結配置為:
https://img-blog.csdn.net/20170226150015627?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxnZW4xNTczODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
因此,如果有自己的Redis配置,請修改,例如下邊的配置:
<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory">
<property name="hostName" value="192.168.1.149"/>
<property name="port" value="6379"/>
<property name="password" value="123456"/>
</bean>
(4)關於Error creating bean with name ‘enableRedisKeyspaceNotificationsInitializer’錯誤的處理:
添加如下配置讓Spring Session不再執行config命令
<util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
如果不添加的話,會報如下錯誤:
Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘enableRedisKeyspaceNotificationsInitializer‘ defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]:
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications.
See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent
Caused by: redis.clients.jedis.exceptions.JedisDataException: ERR unknown command config
(5)在web.xml中添加DelegatingFilterProxy
<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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
DelegatingFilterProxy將通過springSessionRepositoryFilter的名稱尋找Bean並將其轉換為過濾器。對於調用DelegatingFilterProxy的每個請求,也將調用springSessionRepositoryFilter。
(6)Spring MVC controller代碼用於測試:
@Controller
@RequestMapping(value = "/spring/session", produces = {ConstString.APP_JSON_UTF_8})
public class SpringSessionDemoController {
@RequestMapping(value = "/setSession.do", method = RequestMethod.GET)public void setSession(HttpServletRequest request, HttpServletResponse response) { String name = request.getParameter("name"); String value = request.getParameter("value"); request.getSession().setAttribute(name, value);}@RequestMapping(value = "/getSession.do", method = RequestMethod.GET)public void getInterestPro(HttpServletRequest request, HttpServletResponse response) { String name = request.getParameter("name"); System.out.println("------" + request.getSession().getAttribute(name));}@RequestMapping(value = "/removeSession.do", method = RequestMethod.GET)public void removeSession(HttpServletRequest request, HttpServletResponse response) { String name = request.getParameter("name"); request.getSession().removeAttribute(name);}
}
https://img-blog.csdn.net/20170226152438708?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxnZW4xNTczODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
(7)測試
訪問連結:http://localhost:8080/spring/session/setSession.do?name=xuiliugen&value=123456
使用工具查看Redis內容:
https://img-blog.csdn.net/20170226154301031?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGxnZW4xNTczODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
可以發現已經有值了!並且有expirations,可以看到箭頭指向的位置,是失效的時間記錄值!
(8)到此,Spring Session的使用已經完成!其他具體的細節請參考:http://git.oschina.net/xuliugen/spring-session-demo 項目原始碼。
總結:
對於分布式環境Session跨域共用的問題,不管是使用開源的架構還是使用自己開發的架構,都需要明白的一個問題是:在Tomcat容器中建立Session是一個很耗費記憶體的事情。因此,我們在自己寫類似架構的時候,我們一定要注意的是,並不是Tomcat為我們建立好了Session之後,我們首先擷取Session然後再上傳到Redis等進行儲存,而是直接有我們自己建立Session,這一點是至關重要的!
額外分享一些自己的收藏:
高可用叢集架構技術進階篇手把手教你玩轉Nginx與Docker
連結: https://pan.baidu.com/s/1A7Z75fN2UR-cteT8fgY5kQ 密碼: yp8e
走向架構師,你必須瞭解的Java虛擬機器進階特性
連結: https://pan.baidu.com/s/1hAPo19keNFHb9ycBctkU2A 密碼: iayw
高並發處理技術老司機帶你玩RabbitMq實現效能倍增
連結: https://pan.baidu.com/s/1nZNYXtqkmEAHPm1JKT2iBg 密碼: y3sa
2018.07.09-T5架構師帶你解讀Spring源碼~手寫SpringMVC實戰
連結: https://pan.baidu.com/s/1-NRFpVQd0TLyzT1SjskXdg 密碼: 6amk
使用Spring Session和Redis解決分布式Session跨域共用問題