Preface


  HttpSession It is throughServlet Container creation and management, imageTomcat/Jetty It's all stored in memory. But let's build an application cluster of component layout, Then make use ofLVS orNginx Load balancing, From the same userHttp Requests will likely be distributed to multiple different applications. That's the question, How to ensure that different applications can share the samesession What about data?? The simplest idea, Is to putsession Data is saved to a unified place outside memory, for exampleMemcached/Redis Etc. That's another question, How to replaceServlet Container creation and managementHttpSession How to achieve it??

  1, utilizeServlet Plug in functions provided by container, customHttpSession Create and manage policies for, And replace the default policy by configuration.
In fact, there have been open source projects for a long time, for examplememcached-session-manager( Can refer to
load balancing+session Share(memcached-session-manager Realization
<http://www.cnblogs.com/youzhibing/p/5094460.html>)
, as well astomcat-redis-session-manager. But there's a drawback, It's about couplingTomcat/Jetty etc.Servlet Container code.

  2, Design aFilter, utilizeHttpServletRequestWrapper, Realize your own
getSession() Method, Take over creation and managementSession Data work.spring-session That's how it works.

Reference resources spring-session One of Preliminary exploration spring-session
<http://feitianbenyue.iteye.com/blog/2326408>


   This blog does not coversession explain, aboutsession Let's check the data by ourselves; aboutspring-session You can gospring Official website search(http://projects.spring.io/spring-session/).

Standalone application

   Let's take a look at the stand-alone application, The application is very simple, Is insession Set variable in, Then get the variables of these settings for display , The specific code is as follows

  pom.xml:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.yzb.lee</groupId>
<artifactId>spring-session</artifactId> <packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version> <name>spring-session Maven Webapp</name>
<url>http://maven.apache.org</url> <properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies>
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId>
<version>1.2</version> </dependency> <dependency> <groupId>junit</groupId>
<artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope>
</dependency> </dependencies> <build> <finalName>spring-session</finalName>
</build> </project> View Code
  web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app> <display-name>Archetype
Created Web Application</display-name> <servlet>
<servlet-name>session</servlet-name> <servlet-class
>com.yzb.lee.servlet.SessionServlet</servlet-class> </servlet>
<servlet-mapping> <servlet-name>session</servlet-name>
<url-pattern>/session</url-pattern> </servlet-mapping> <welcome-file-list>
<welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> View Code
  SessionServlet.java
package com.yzb.lee.servlet; import java.io.IOException; import
javax.servlet.ServletException;import javax.servlet.http.HttpServlet; import
javax.servlet.http.HttpServletRequest;import
javax.servlet.http.HttpServletResponse;public class SessionServlet extends
HttpServlet {private static final long serialVersionUID = 1L; @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException { String attributeName=
req.getParameter("attributeName"); String attributeValue =
req.getParameter("attributeValue");
req.getSession().setAttribute(attributeName, attributeValue);
resp.sendRedirect(req.getContextPath()+ "/"); } } View Code
  index.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page
isELIgnored="false" %> <!DOCTYPE html> <html lang="en"> <head> <title>Session
Attributes</title> </head> <body> <divclass="container"> <h1>Description</h1>
<p>This application demonstrates how to use a Redis instance to back your
session. Notice that there is no JSESSIONID cookie. We are also able to
customize the way of identifying what the requested session id is.</p> <h1>Try
it</h1> <formclass="form-inline" role="form" action="./session" method="post">
<labelfor="attributeName">Attribute Name</label> <input id="attributeName"
type="text" name="attributeName"/> <labelfor="attributeValue">Attribute
Value</label> <input id="attributeValue" type="text" name="attributeValue"/>
<input type="submit" value="Set Attribute"/> </form> <hr/> <tableclass="table
table-striped"> <thead> <tr> <th>Attribute Name</th> <th>Attribute Value</th>
</tr> </thead> <tbody> <c:forEach items="${sessionScope}" var="attr"> <tr>
<td><c:out value="${attr.key}"/></td> <td><c:out value="${attr.value}"/></td>
</tr> </c:forEach> </tbody> </table> </div> </body> </html> View Code
   The whole project structure is very simple, As follows

  

   Run locally, The effect is as follows



   Firefox and360 Browsers represent different users, Each can get its ownsession All variables set in, Normal, Nothing wrong with it..

Distributed cluster application

   In stand-alone application,session No problem, It's localservlet Container, Is it as normal as a single machine in a distributed cluster? Let's look down

   Build highly available, The distributed cluster environment for load balancing can be referred tonginx Load balancing of requests + keepalived Realizationnginx High availability
<http://www.cnblogs.com/youzhibing/p/7327342.html>, What we didn't build needs to build the distributed environment first

   Not set upession Share

     Application unchanged, The code is exactly the same as that in a single machine, Deploy the code to the distributed cluster

     Run it all, The effect is as follows



     The result is: No mattersession How many values to set,session None of the values in could be obtained( There is still a gap from my expectation, Please see my question about the specific gap)

  spring-session Realizationsession Share

     Application changes, Code is different from before, The specific differences are as follows(SessionServlet Andindex.jsp Unchanged)

    pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.yzb.lee</groupId>
<artifactId>spring-session</artifactId> <packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version> <name>spring-session Maven Webapp</name>
<url>http://maven.apache.org</url> <properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies>
<dependency> <groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.3.1.RELEASE</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>4.3.4.RELEASE</version> </dependency> <dependency>
<groupId>javax.servlet</groupId> <artifactId>jstl</artifactId>
<version>1.2</version> </dependency> <dependency> <groupId>junit</groupId>
<artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope>
</dependency> </dependencies> <build> <finalName>spring-session</finalName>
</build> </project> View Code
    web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app> <display-name>Archetype
Created Web Application</display-name> <!-- spring-session config -->
<context-param> <param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-session.xml</param-value> </context-param> <!--
thisfilter Put it first --> <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> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <servlet> <servlet-name>session</servlet-name>
<servlet-class>com.yzb.lee.servlet.SessionServlet</servlet-class> </servlet>
<servlet-mapping> <servlet-name>session</servlet-name>
<url-pattern>/session</url-pattern> </servlet-mapping> <welcome-file-list>
<welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> View Code
    spring-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" xmlns:p
="http://www.springframework.org/schema/p" xsi:schemaLocation
="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://
www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config /> <!-- Loadproperties file --> <bean
id="configProperties"class
="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"> <list>
<value>classpath:session-redis.properties</value> </list> </property> </bean>
<!-- RedisHttpSessionConfiguration --> <bean class
="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="${redis.session.timeout}"
/> <!-- session Expiration date, Units are seconds. --> </bean> <!--LettuceConnectionFactory --> <bean
class
="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"
/> </beans> View Code
    session-redis.properties
redis.host=192.168.0.221 redis.pass=myredis redis.port=6379
redis.session.timeout=600
     The overall project structure is as follows

    

     Deploy the code to the distributed cluster, Run again, The effect is as follows

 


     The effect is the same as that of single machine application, That meanssession Shared implementation, Let's seeredis Are there anysession data, Following chart,redis Medium exists.session Information

    

  session colony


     One in frontredis The server:192.168.0.221 Donesession The server, If there is only one, it will cause a single point of failure, So the wholesession The service is gone, Too much impact. To avoid a single point of failure, Need to build asession colony. When building a cluster, Do not open login authentication(requirepass Comment do not open, Specific reasons will be explained later)

    redis Cluster environment

      192.168.0.221:3 Node(7000,7001,7002)

      192.168.0.223:3 Node(7003,7004,7005)

    redis For the process of cluster construction, please refer toRedis Cluster building and simple use
<http://www.cnblogs.com/wuxl360/p/5920330.html>

    redis After each node is built successfully, The starting conditions are as follows

    192.168.0.221

      

    192.168.0.223

      

    # ./redis-trib.rb create  --replicas  1  192.168.0.221:7000
192.168.0.221:7001  192.168.0.221:7002 192.168.0.223:7003  192.168.0.223:7004
 192.168.0.223:7005

      Whatever it is(192.168.0.221,192.168.0.223 Any one of them) Just execute the above command, If the following information appears, It means that the cluster is built successfully

    


    redis The cluster has been set up, The next step is toredis Cluster application in our project, The code is inspring-sesson Realizationsession Based on sharing, There are only different documentsspring-session.xml andsession-redis.properties

    spring-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" xmlns:p
="http://www.springframework.org/schema/p" xsi:schemaLocation
="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://
www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config /> <!-- Loadproperties file --> <bean
id="configProperties"class
="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"> <list>
<value>classpath:session-redis.properties</value> </list> </property> </bean>
<!-- RedisHttpSessionConfiguration --> <bean class
="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="${redis.session.timeout}"
/> <!-- session Expiration date, Units are seconds. --> </bean> <!--JedisConnectionFactory --> <beanclass
="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg> <!--redisCluster To configure--> <beanclass
="org.springframework.data.redis.connection.RedisClusterConfiguration">
<constructor-arg> <list> <value>${redis.master1}</value>
<value>${redis.master2}</value> <value>${redis.master3}</value> </list>
</constructor-arg> </bean> </constructor-arg> </bean>
<!--LettuceConnectionFactory --> <!-- Single noderedis --> <!-- <bean class
="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"
/> --> </beans> View Code
    session-redis.properties
#redis.host=192.168.0.221 #redis.pass=myredis #redis.port=6379 redis.master1
=192.168.0.221:7000 redis.master2=192.168.0.223:7003 redis.master3
=192.168.0.223:7004 redis.session.timeout=600 View Code
     According to my pro test, Effect and single noderedis The effect is the same, I won't put the renderings, But you'd better go and have a test.

     Project address:spring-session <https://github.com/youzhibing/spring-session>

Attention points


  1, In stand-alone application,HttpSession It is throughServlet Container creation and management,servlet Once the container stops serving, thatsession And then it disappeared; But ifsession Be saved toredis in, as long asredis The service didn't stop andsession During the validity period, thatservlet Container is out of service,session It still exists, What's the good about that, The advantage isservlet Flash stop and flash repair of container, Users don't have to log in again.


  2,spring MediumContextLoaderListener AndDispatcherServlet I don't know. Do you know, Strictly speaking, these two are responsible for loadingbean There's a difference, It's also better to set it to load differentbean, Or something unexpected may happen to you. If you don't know the difference, you can read it
Talking aboutContextLoaderListener Its context andDispatcherServlet Difference
<http://www.cnblogs.com/weknow619/p/6341395.html>.

  3, When testing, you can test from the bottom to the top, That is to say, test firsttomcat, Retestnginx, Final testVIP.

  4,redis Can be deleted manually insession, It doesn't have to waitsession Be overdue.


  5, When distributed testing, Best inindex.jsp Add some marks( for exampleip, Write deadindex.jsp On the serverip), Used to distinguish different servers, It's more obvious to test that way.


  6,spring-session In the example provided on the official website, Configured by annotation, But I didn't see it at allweb.xml There arespring Configuration, But actuallyspring Container started, And instantiate the requiredbean, Applications can also run, It's hard for me to understand,spring When was the container initialized?
This is actuallyservlet3.0 New features,servlet3.0 Start support noneweb.xml Annotation configuration mode of, andAbstractHttpSessionApplicationInitializer(
AbstractHttpSessionApplicationInitializer implements WebApplicationInitializer
) It's the access point( Just likeweb.xml Middle configurationspring equally), More details need to be consulted.

  7, Set upredis When clustering, If password login is set( takeredis.conf inrequirepass Open and set your own password), Then execute#
./redis-trib.rb create  --replicas  1  192.168.0.221:7000 192.168.0.221:7001
 192.168.0.221:7002 192.168.0.223:7003  192.168.0.223:7004
 192.168.0.223:7005 It will prompt[ERR] Sorry, can't connect to node 192.168.0.221:7000,
Then we need to/usr/lib/ruby/gems/1.8/gems/redis-3.3.0/lib/redis/client.rb Mediumpassword Just change your password, Yes, of course,redis All instances of must have the same password, Or allredis.conf The value of password setting in should be the same, modify
/usr/lib/ruby/gems/1.8/gems/redis-3.3.0/lib/redis/client.rb as follows
vim /usr/lib/ruby/gems/1.8/gems/redis-3.3.0/lib/redis/client.rb
takeclient.rb Mediumpassword Change to your ownredis Passwordclass Redis class Client DEFAULTS = { :url =>
lambda { ENV["REDIS_URL"] }, :scheme => "redis", :host => "127.0.0.1", :port =>
6379, :path => nil, :timeout => 5.0, :password => "myredis", # Change to your own password :db => 0,
:driver=> nil, :id => nil, :tcp_keepalive => 0, :reconnect_attempts => 1,
:inherit_socket=> false }
     I said before, utilizeredis Cluster to storesession When, Do not open login authentication, becausejedis I don't think soredis Cluster password settings for.

problem


  1, Not set for distributed clustersession In case of sharing, Why can't one of the set values get, According to my understanding, the data returned each time should be sometomcat Uppersession Data in, When there are more values set, Every time there should be a value returned, And the test results are no matter how many values you set, No value returned, I don't know why.

  2,jedis Set the cluster password like this, Not yet known, Please leave a message if you know;
Or knowlettuce How to set upredis Cluster and cluster password can also leave a message; Or in any other way, you can leave a message; Thank you!

Reference resources

  spring-session One of Preliminary exploration spring-session
<http://feitianbenyue.iteye.com/blog/2326408>

   utilizespring session Solution sharingSession problem
<http://blog.csdn.net/patrickyoung6625/article/details/45694157>

  【Spring】 Talking aboutContextLoaderListener Its context andDispatcherServlet Difference
<http://www.cnblogs.com/weknow619/p/6341395.html>

  Spring Session <http://projects.spring.io/spring-session/>

   explore Spring 3.1 the simplest and most common charactersweb.xml type Code based configurationservlet3.0 application
<http://blog.csdn.net/palmtale/article/details/8283521>

  Redis cluster tutorial <https://redis.io/topics/cluster-tutorial>

  Redis Cluster
<http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#cluster>

  Redis Cluster building and simple use <http://www.cnblogs.com/wuxl360/p/5920330.html>