spring-session管理集群环境中session共享

session共享

说到集群,很多人知道其的意义。集群就是多个分布服务器节点,然代理服务器根据权重自动选择适合的节点分配任务。这样就可以开发出高性能的应用。不知道怎么配置的请看我这些文章:(nginx+tomcat: https://blog.csdn.net/lwg_1540652358/article/details/83714343  )或者(apache+tomcat: https://blog.csdn.net/lwg_1540652358/article/details/83856480)  ;但是在这个过程中,session的共享是个令人头疼的问题,虽然可以在tomcat自身配置session共享,但是这样效益不高。

spring-session

今天,我们就来讲一下通过Spring Session+Redis实现Session共享。 spring-session是属于spring家族中的一员,适合用来管理分布式集群中的session共享。Session一直是我们做集群时一个比较头疼的问题,之前有一个GitHub上的开源控件tomcat-redis-session-manager,但是这个东西目前只支持到Tomcat7,不是最佳选项,我们也可以使用Nginx提供的ip_tables,将同一个请求ip定位到同一台服务器上,但是这样没有办法充分利用服务集群的性能,Spring Session的出现可以很好的帮助我们解决这些问题,它具有如下特点:
 

单机应用:

单机应用中,session肯定没问题,就存在本地的servlet容器中,那么在分布式集群中会像单机一样正常吗?我们接着往下看

分布式集群应用:

分布式中每一个节点都可以做集群处理,在这过程中如何在每一个节点中做到session一致是个问题,(如两个tomcat,如何在这两个中间session公用)。接下来直接进入spring-session的配置

spring-session的配置:

(1)创建号maven项目,在Pom文件加上以下依赖:


        <!-- Jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <!-- Spring Data Redis -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.8.4.RELEASE</version>
        </dependency>
        <!-- Spring Session -->
        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session</artifactId>
            <version>1.3.1.RELEASE</version>
        </dependency>
        <!-- Apache Commons Pool -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
  

(2)配置好你的环境,如使用spring+springmvc环境:

这里就简单的给出spring+springmvc的web.xml的配置:

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

然后在applicationContext.xml文件中简单的配置一下spring,如下:

<context:component-scan base-package="org.sang" use-default-filters="false">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

配置Spring容器要扫描的注解。 
然后在spring-servlet.xml中配置SpringMVC容器要扫描的注解,如下:

<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <mvc:annotation-driven/>

    <context:component-scan base-package="org.sang" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

</beans>
 

(3)废话不多说,配置好了的直接忽略上一步.开始正式进入spring-session,配置web.xml

下面过滤器一般来说配置在第一个

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

这里使用了Spring Web提供的代理过滤器,将拦截到的请求全部交给一个名为springSessionRepositoryFilter的过滤器进行处理。OK,然后在applicationContext.xml中配置Spring Session和Redis,如下:

<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
    <bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="192.168.248.128"/>
        <property name="port" value="6379"/>
        <property name="database" value="0"/>
    </bean>
 

hostName即为redis的地址,port为redis的服务端口。

就是这么简单,这样就配置好了。

那如何在实际中使用它呢?

Spring Session对HTTP的支持所依靠的是一个简单老式的ServletFilter,借助servlet规范中标准的特性来实现Spring Session的功能。因此,我们能够让已有的war文件使用Spring Session的功能,而无需修改已有的代码,当然如果你使用javax.servlet.http.HttpSessionListener的话,就另当别论了。Spring Session 1.0并不支持HttpSessionListener ,但是Spring Session 1.1 M1发布版本已经添加了对它的支持,你可以通过该地址了解更多细节信息。

使用Spring Session

Spring Session配置完成之后,我们就可以使用标准的Servlet API与之交互了。例如,如下的代码定义了一个servlet,它使用标准的Servlet session API来访问session。

@WebServlet("/example")
public class Example extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    // 使用正常的servlet API获取session,在底层,
    // session是通过Spring Session得到的,并且会存储到Redis或
    // 其他你所选择的数据源中

    HttpSession session = request.getSession();
    String value = session.getAttribute(“someAttributeâ€);

  }
}

每个浏览器多个Session

Spring Session会为每个用户保留多个session,这是通过使用名为“_s”的session别名参数实现的。例如,如果到达的请求为http://example.com/doSomething?_s=0 ,那么Spring Session将会读取“_s”参数的值,并通过它确定这个请求所使用的是默认session。

如果到达的请求是http://example.com/doSomething?_s=1的话,那么Spring Session就能知道这个请求所要使用的session别名为1.如果请求没有指定“_s”参数的话,例如http://example.com/doSomething,那么Spring Session将其视为使用默认的session,也就是说_s=0

要为某个浏览器创建新的session,只需要调用javax.servlet.http.HttpServletRequest.getSession()就可以了,就像我们通常所做的那样,Spring Session将会返回正确的session或者按照标准Servlet规范的语义创建一个新的session。下面的表格描述了针对同一个浏览器窗口,getSession()面对不同url时的行为。

HTTP请求URL

Session别名

getSession()的行为

example.com/resource

0

如果存在session与别名0关联的话,就返回该session,否则的话创建一个新的session并将其与别名0关联。

example.com/resource?_s=1

1

如果存在session与别名1关联的话,就返回该session,否则的话创建一个新的session并将其与别名1关联。

example.com/resource?_s=0

0

如果存在session与别名0关联的话,就返回该session,否则的话创建一个新的session并将其与别名0关联。

example.com/resource?_s=abc

abc

如果存在session与别名abc关联的话,就返回该session,否则的话创建一个新的session并将其与别名abc关联。

如上面的表格所示,session别名不一定必须是整型,它只需要区别于其他分配给用户的session别名就可以了。但是,整型的session别名可能是最易于使用的,Spring Session提供了HttpSessionManager接口,这个接口包含了一些使用session别名的工具方法。

我们可以在HttpServletRequest中,通过名为“org.springframework.session.web.http.HttpSessionManager”的属性获取当前的HttpSessionManager。如下的样例代码阐述了如何得到HttpSessionManager,并且在样例注释中描述了其关键方法的行为。

复制代码

@WebServlet("/example")
public class Example extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request,HttpServletResponse response)
  throws ServletException, IOException {

    /*
     * 在请求中,根据名为org.springframework.session.web.http.HttpSessionManager的key
     * 获得Spring Session session管理器的引用
     */

    HttpSessionManager sessionManager=(HttpSessionManager)request.getAttribute(
        "org.springframework.session.web.http.HttpSessionManager");

    /*
     * 使用session管理器找出所请求session的别名。
     * 默认情况下,session别名会包含在url中,并且请求参数的名称为“_s”。
     * 例如,http://localhost:8080/example?_s=1
     * 将会使如下的代码打印出“Requested Session Alias is: 1”
     */
    String requestedSessionAlias=sessionManager.getCurrentSessionAlias(request);
    System.out.println("Requested Session Alias is:  " + requestedSessionAlias);

    /* 返回一个唯一的session别名id,这个别名目前没有被浏览器用来发送请求。
     * 这个方法并不会创建新的session,
     * 我们需要调用request.getSession()来创建新session。
     */
    String newSessionAlias = sessionManager.getNewSessionAlias(request);

    /* 使用新创建的session别名来建立URL,这个URL将会包含
     * “_s”参数。例如,如果newSessionAlias的值为2的话, 
     * 那么如下的方法将会返回“/inbox?_s=2”
     */

    String encodedURL = sessionManager.encodeURL("/inbox", newSessionAlias);
    System.out.println(encodedURL);

    /* 返回session别名与session id所组成的Map,
    * 它们是由浏览器发送请求所形成的。
     */
    Map < String, String > sessionIds = sessionManager.getSessionIds(request);
  }
}

结论

Spring Session为企业级Java的session管理带来了革新,使得如下的任务变得更加容易:

  • 编写可水平扩展的原生云应用。
  • 将session所保存的状态卸载到特定的外部session存储中,如Redis或Apache Geode中,它们能够以独立于应用服务器的方式提供高质量的集群。
  • 当用户使用WebSocket发送请求的时候,能够保持HttpSession处于活跃状态。
  • 在非Web请求的处理代码中,能够访问session数据,比如在JMS消息的处理代码中。
  • 支持每个浏览器上使用多个session,这样就可以很容易地构建更加丰富的终端用户体验。
  • 控制客户端和服务器端之间如何进行session id的交换,这样更加易于编写Restful API,因为它可以从HTTP 头信息中获取session id,而不必再依赖于cookie。

如果你想抛弃传统的重量级应用服务器,但受制于已经使用了这些应用服务器的session集群特性,那么Spring Session将是帮助你迈向更加轻量级容器的重要一步,这些轻量级的容器包括Tomcat、Jetty或Undertow。

猜你喜欢

转载自blog.csdn.net/lwg_1540652358/article/details/84110711
今日推荐