任何一種技術的出現,都是來解決特定的問題的!html
本篇開始學習Spring-Session相關的一些知識學習整理,讓咱們開始吧!html5
HttpSession是經過Servlet容器進行建立和管理的,在單機環境中。經過Http請求建立的Session信息是存儲在Web服務器內存中,如Tomcat/Jetty。java
假如當用戶經過瀏覽器訪問應用服務器,session信息中保存了用戶的登陸信息,而且session信息沒有過時失,效那麼用戶就一直處於登陸狀態,能夠作一些登陸狀態的業務操做!
nginx
可是如今不少的服務器都採用分佈式集羣的方式進行部署,一個Web應用,可能部署在幾臺不一樣的服務器上,經過LVS或者Nginx等進行負載均衡(通常使用Nginx+Tomcat實現負載均衡)。此時來自同一用戶的Http請求將有可能被分發到不一樣的web站點中去(如:第一次分配到A站點,第二次可能分配到B站點)。那麼問題就來了,如何保證不一樣的web站點可以共享同一份session數據呢?git
假如用戶在發起第一次請求時候訪問了A站點,並在A站點的session中保存了登陸信息,當用戶第二次發起請求,經過負載均衡請求分配到B站點了,那麼此時B站點可否獲取用戶保存的登陸的信息呢?答案是不能的,由於上面說明,Session是存儲在對應Web服務器的內存的,不能進行共享,此時Spring-session就出現了,來幫咱們解決這個session共享的問題!github
簡單點說就是請求http請求通過Filter職責鏈,根據配置信息過濾器將建立session的權利由tomcat交給了Spring-session中的SessionRepository,經過Spring-session建立會話,並保存到對應的地方。web
實際上實現Session共享的方案不少,其中一種經常使用的就是使用Tomcat、Jetty等服務器提供的Session共享功能,將Session的內容統一存儲在一個數據庫(如MySQL)或緩存(如Redis,Mongo)中,redis
而上面說的使用Nginx也能夠,使用ip_hash策略。
【Nginx】實現負載均衡的幾種方式
在使用Nginx的ip_hash策略時候,每一個請求按訪問ip的hash結果分配,這樣每一個訪客固定訪問一個後端服務器,也能夠解決session的問題。spring
Spring會話提供了與HttpSession的透明集成,容許以應用程序容器(即Tomcat)中性的方式替換HttpSession,可是咱們從中獲得了什麼好處呢?數據庫
RESTful api——Spring會話容許在header中提供會話id以使用RESTful api。
Spring Session & WebSockets的完美集成。
整個項目的總體骨架:
本次只講解xml配置方式,javaConfig配置能夠參考官方文檔:Spring Java Configuration
本次項目須要用戶Nginx和Redis,若是沒有配置Nginx的請看這裏: Windows上Nginx的安裝教程詳解
沒有配置Redis的請看這裏:Redis——windows環境安裝Redis和Redis sentinel部署教程
配置好了上面的環境,後下面開始正式的Spring-session搭建過程了!
首先新建一個Maven的Web項目,新建好以後在pom文件中添加下面的依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.spring</groupId> <artifactId>learn-spring-session</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>First Learn Spring Session</name> <properties> <jdk.version>1.7</jdk.version> <spring.version>4.3.4.RELEASE</spring.version> <spring-session.version>1.3.1.RELEASE</spring-session.version> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>${spring-session.version}</version> <type>pom</type> </dependency> <dependency> <groupId>biz.paluch.redis</groupId> <artifactId>lettuce</artifactId> <version>3.5.0.Final</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </project>
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/*xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--DelegatingFilterProxy將查找一個Bean的名字springSessionRepositoryFilter丟給一個過濾器。爲每一個請求 調用DelegatingFilterProxy, springSessionRepositoryFilter將被調用--> <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> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
在resources 下面新建一個xml,名詞爲 application-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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:annotation-config/> <!--建立一個Spring Bean的名稱springSessionRepositoryFilter實現過濾器。 篩選器負責將HttpSession實現替換爲Spring會話支持。在這個實例中,Spring會話獲得了Redis的支持。--> <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/> <!--建立了一個RedisConnectionFactory,它將Spring會話鏈接到Redis服務器。咱們配置鏈接到默認端口(6379)上的本地主機!--> <bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/> </beans>
新建 LoginServlet.java
@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; request.getSession().setAttribute("testKey", "742981086@qq.com"); request.getSession().setMaxInactiveInterval(10*1000); response.sendRedirect(request.getContextPath() + "/session"); } }
新建 SessionServlet.java
@WebServlet("/session") public class SessionServlet extends HttpServlet { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; System.out.println(request.getRemoteAddr()); System.out.print(request.getRemoteHost() + " : " + request.getRemotePort()); String sesssionID = request.getSession().getId(); System.out.println("-----------tomcat2---sesssionID-------" + sesssionID); String testKey = (String)request.getSession().getAttribute("testKey"); System.out.println("-----------tomcat2-testKey-------" + testKey); PrintWriter out = null; try { out = response.getWriter(); out.append("tomcat2 ---- sesssionID : " + sesssionID); out.append("{\"name\":\"dufy2\"}" + "\n"); out.append("tomcat2 ----- testKey : " + testKey + "\n"); }catch (Exception e){ e.printStackTrace(); }finally { if(out != null){ out.close(); } } } }
Nginx的配置,輪詢方式:
#user nobody; worker_processes 1; events{ worker_connections 1024; } http{ upstream myproject { server 127.0.0.1:8888; server 127.0.0.1:9999; } server { listen 8080; server_name localhost; location / { proxy_pass http://myproject; } } }
將上面搭建好的項目放入兩個Tomcat中,分別啓動。使用Nginx負載均衡均驗證Session是否共享成功!
tomcat1 : http://localhost:8888/
tomcat2 : http://localhost:9999/
訪問 http://localhost:8080/ 能夠看到,每次刷新頁面,請求都分發到不一樣的Tomcat裏面,如圖
而後使用 http://localhost:8080/login 看到地址欄重定向到/session .發現瀏覽器返回了數據,進入tomcat2中,而後再次刷新請求進入了tomcat1,發現tomcat2中和tomcat1 sessionId同樣,而且在tomcat1中存的testKey,在Tomcat2中也能夠獲取,不只SessionId能夠共享,其餘一些數據也能夠進行共享!
如何在Redis中查看Session數據,可使用命令,或者在Windows的RedisDesktopManager中查看!
key的簡單介紹說明:
# 存儲 Session 數據,數據類型hash Key:spring:session:sessions:XXXXXXX # Redis TTL觸發Session 過時。(Redis 自己功能),數據類型:String Key:spring:session:sessions:expires:XXXXX #執行 TTL key ,查看剩餘生存時間 #定時Job程序觸發Session 過時。(spring-session 功能),數據類型:Set Key:spring:session:expirations:XXXXX
驗證成功!
Spring-Session在實際的項目中使用仍是比較普遍的,本次搭建採用最簡單的配置,基本都是採用官方文檔中默認的方式,由於是入門的教程就沒有寫的很複雜,後面慢慢深刻!期待後續的教程吧!
使用Spring Session和Redis解決分佈式Session跨域共享問題57406162
學習Spring-Session+Redis實現session共享
【第一篇】Spring-Session實現Session共享入門教程
【第二篇】Spring-Session實現Session共享Redis集羣方式配置教程【更新中...請期待...】
【第三篇】Spring-Session實現Session共享實現原理以及源碼解析【更新中...請期待...】
本系列的源碼下載地址:learn-spring-session-core
備註: 因爲本人能力有限,文中如有錯誤之處,歡迎指正。
謝謝你的閱讀,若是您以爲這篇博文對你有幫助,請點贊或者喜歡,讓更多的人看到!祝你天天開心愉快!