分布式Session组件

背景

web或app服务器集群承担了对外服务的首要职责,其中会话(session)是跟踪用户操作流程的重要手段,当中保存了众多用户访问相关的数据。一般有以下几个手段来保证用户访问期间的session一致性:

  1. 将一个用户的访问始终分配到同一台服务器上

主要是通过配置前端负载均衡设备来保证,通过追踪客户的IP地址、cookie等方法来识别客户,并将客户的会话保持在一台服务器上。优点是简单,应用层改造成本小;缺点是对负载均衡设备依赖高,服务器负载无法完全分散,对于NAT,3G等客户端复杂网络环境比较难以处理。

  1. 将用户的会话信息复制到集群内所有服务器

主要是通过web容器提供的功能来进行配置,例如tomcat和weblogic的session复制功能等。优点是配置简单,对客户端的复杂网络变化不敏感;缺点是当系统内集群数量较多时,会话复制耗费大量的服务器资源和网络带宽,扩展性较差。

  1. 将用户的会话信息集中保存到容器外的某个位置

主要是通过memcached或redis等分布式缓存服务器来存储用户会话信息,每台应用服务器在处理用户会话时从缓存服务器获取和写入。优点是系统容量和扩展性高,不受制于硬件或软件;缺点是应用需要做的改动较大(重新实现会话管理的接口),而且单个会话的访问延迟相比本机访问有所上升。

综合评估并借鉴互联网领域先行者的经验之后,我们认为方案3是面向海量用户的互联网应用应当采用的技术方案。为了简化应用配置和开发工作,我们选择封装自己的分布式session管理组件。

简介

spring-session-redis(svn地址) 是在spring-session基础上进行封装的一款分布式session管理组件,通过简单的配置即可让web应用支持基于reids的分布式session。

spring-session的实现原理是通过重写HttpSession接口,并利用filter机制用经过改写的分布式会话对象替换web容器的本地实现,于是所有session相关的操作均由spring-session接管。详情请参考spring-session文档

配置手册

  1. maven项目的pom.xml文件中增加spring-session-redis依赖:
<dependency>
	<groupId>com.cib.fintech.framework</groupId>
	<artifactId>spring-session-redis</artifactId>
	<version>1.0.0-SNAPSHOT</version>
</dependency>

至少需要spring3.2.14以上版本springframework支持

  1. 在项目的spring配置文件(例如:applicationContext.xml)中增加如下内容:
<!-- spring session 配置 -->
<import resource="classpath*:**/application-redis-session.xml" />

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<list>
		    <!-- spring session 配置 -->
			<value>classpath*:*/session.properties</value> 
		</list>
	</property>
</bean>
  1. 在项目的classpath中增加session.properties文件,配置如下内容:
#redis 会话最大失效时间间隔
session.redis.maxInActiveInterval=1800
#应用使用redis存储空间前缀(可多个应用共享session)
session.redis.namespace=serviceName
#分布式session写入策略
#session.redis.flushmode=IMMEDIATE
session.redis.flushmode=ON_SAVE
#redis服务器信息
session.redis.host=127.0.0.1
session.redis.port=6379
session.redis.password=
#默认redis数据库
session.redis.default.db=0
#redis通信配置参数
session.redis.timeout=100000
session.redis.maxActive=300
session.redis.maxIdle=100
session.redis.maxWait=1000
  1. 在项目的web.xml文件中增加分布式session管理的filter
<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>

注意,这个filter的顺序应当放在最前

  1. 启动服务

常见问题

应用在spring mvc项目中,静态资源访问也造成读取redis问题

使用spring session时,即使是静态的资源,也会去访问redis。原因是spring session的filter配置规则为拦截所有请求:

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

可以加入一个自定义的filter在springSessionRepositoryFilter前,排除静态资源

<filter>
	  <filter-name>resourceExcludingFilter</filter-name>
	  <filter-class>com.cib.fintech.one.web.session.SpringSessionResourceExcludingFilter</filter-class>
	</filter>
	<filter-mapping>
	  <filter-name>resourceExcludingFilter</filter-name>
	  <url-pattern>/resources/*</url-pattern>
	</filter-mapping>
public class SpringSessionResourceExcludingFilter implements Filter {

    private static final String RESOURCE_PATH = "/resources/";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;

        String path = req.getServletPath();
        if (path.startsWith(RESOURCE_PATH)) {
            request.getRequestDispatcher(path).forward(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
    }
}
相关文章

相关标签/搜索