天天看點

Spring-Session實作Session共享入門教程

Spring Session 入門的教程,了解為什麼要使用Spring session也很重要!

任何一種技術的出現,都是來解決特定的問題的!

本篇開始學習Spring-Session相關的一些知識學習整理,讓我們開始吧!

Spring-Session介紹

  1. Spring-Session使用的場景?

HttpSession是通過Servlet容器進行建立和管理的,在單機環境中。通過Http請求建立的Session資訊是存儲在Web伺服器記憶體中,如Tomcat/Jetty。

假如當使用者通過浏覽器通路應用伺服器,session資訊中儲存了使用者的登入資訊,并且session資訊沒有過期失,效那麼使用者就一直處于登入狀态,可以做一些登入狀态的業務操作!

但是現在很多的伺服器都采用分布式叢集的方式進行部署,一個Web應用,可能部署在幾台不同的伺服器上,通過LVS或者Nginx等進行負載均衡(一般使用Nginx+Tomcat實作負載均衡)。此時來自同一使用者的Http請求将有可能被分發到不同的web站點中去(如:第一次配置設定到A站點,第二次可能配置設定到B站點)。那麼問題就來了,如何保證不同的web站點能夠共享同一份session資料呢?

假如使用者在發起第一次請求時候通路了A站點,并在A站點的session中儲存了登入資訊,當使用者第二次發起請求,通過負載均衡請求配置設定到B站點了,那麼此時B站點能否擷取使用者儲存的登入的資訊呢?答案是不能的,因為上面說明,Session是存儲在對應Web伺服器的記憶體的,不能進行共享,此時Spring-session就出現了,來幫我們解決這個session共享的問題!

  1. 如何進行Session共享呢?

簡單點說就是請求http請求經過Filter職責鍊,根據配置資訊過濾器将建立session的權利由tomcat交給了Spring-session中的SessionRepository,通過Spring-session建立會話,并儲存到對應的地方。

實際上實作Session共享的方案很多,其中一種常用的就是使用Tomcat、Jetty等伺服器提供的Session共享功能,将Session的内容統一存儲在一個資料庫(如MySQL)或緩存(如Redis,Mongo)中,

而上面說的使用Nginx也可以,使用ip_hash政策。

【Nginx】實作負載均衡的幾種方式

在使用Nginx的ip_hash政策時候,每個請求按通路ip的hash結果配置設定,這樣每個訪客固定通路一個後端伺服器,也可以解決session的問題。

  1. Spring官方介紹

    Why Spring Session & HttpSession?

Spring會話提供了與HttpSession的透明內建,允許以應用程式容器(即Tomcat)中性的方式替換HttpSession,但是我們從中得到了什麼好處呢?

  • 叢集會話——Spring會話使支援叢集會話變得微不足道,而不需要綁定到應用程式容器的特定解決方案。
  • 多個浏覽器會話——Spring會話支援在單個浏覽器執行個體中管理多個使用者會話(也就是多個經過驗證的帳戶,類似于谷歌)。
  • RESTful api——Spring會話允許在header中提供會話id以使用RESTful api。
  • Spring Session & WebSockets的完美內建。

項目搭建

整個項目的整體骨架:

基于XML配置方式的Spring Session

本次隻講解xml配置方式,javaConfig配置可以參考官方文檔:Spring Java Configuration

環境說明

本次項目需要使用者Nginx和Redis,如果沒有配置Nginx的請看這裡: Windows上Nginx的安裝教程詳解

沒有配置Redis的請看這裡:Redis——windows環境安裝Redis和Redis sentinel部署教程

配置好了上面的環境,後下面開始正式的Spring-session搭建過程了!

1.添加項目依賴

首先建立一個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>


           

2.web.xml配置

<?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>
        
        
           

3.Xml的配置

在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>

           

4.測試代碼

建立 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", "[email protected]");

        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();
            }
        }

    }

}

           

效果示範

1.啟動Redis,預設端口6379就行!

2.配置Nginx,啟動Nginx

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;
            }
        }
}

           

3.啟動Tomcat1和Tomcat2

将上面搭建好的項目放入兩個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共享入門教程

【第二篇】Spring-Session實作Session共享Redis叢集方式配置教程【更新中...請期待...】

【第三篇】Spring-Session實作Session共享實作原理以及源碼解析【更新中...請期待...】

本系列的源碼下載下傳位址:learn-spring-session-core

備注: 由于本人能力有限,文中若有錯誤之處,歡迎指正。

謝謝你的閱讀,如果您覺得這篇博文對你有幫助,請點贊或者喜歡,讓更多的人看到!祝你每天開心愉快!

Java程式設計技術樂園

:一個分享程式設計知識的公衆号。跟着老司機一起學習幹貨技術知識,每天進步一點點,讓小的積累,帶來大的改變!

掃描關注,背景回複

【秘籍】

,擷取珍藏幹貨!

99.9%

的夥伴都很喜歡

Spring-Session實作Session共享入門教程

© 每天都在變得更好的阿飛雲

版權聲明

作者:阿飛雲

出處:部落格園阿飛雲的技術部落格--http://www.cnblogs.com/aflyun

您的支援是對部落客最大的鼓勵,感謝您的認真閱讀。

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀