单机session共享问题的解决

来源:互联网 发布:网络视频地址 编辑:程序博客网 时间:2024/04/28 11:10
在前台验证码使用servlet写的或者登录数据放入session中,但是当项目要发生产环境的话会产生该问题,原因是session只存在于单机如果多台服务器的话,在登录或者验证码等问题从session获取值时报错。
以下以验证码为例。
环境现场代码:
1、web.xml中配置
  <!-- 配置验证码servlet -->
    <servlet>
        <servlet-name>validateCode</servlet-name>
        <servlet-class>com.msok.insure.servlet.ValidateCodeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>validateCode</servlet-name>
        <url-pattern>/ValidateCodeServlet.do</url-pattern>
    </servlet-mapping>
  <servlet>
2、controller中servlet类-源码
http://note.youdao.com/share/?id=319bebf8110c29152515892b655db0ff&type=note
3、可以采用什么方式避免呢有以下几种想法
第一种想法:可以用分布式缓存     

分布式环境下的session需要解决如下几个问题: 
1.会话数据更新的同步 
2.会话过期的同步 
3.会话删除的同步 
4.会话access的同步

第二种想法:Tomcat+memcached/redis
  将session信息保存到缓存中生成唯一的key-value,然后在cookie中保存key,用户请求时通过key去缓存中获取session信息
第三种想法:
  1.不使用session,session中的数据采用加密存放到cookie中 
        2.前段负载均衡按照请求IP分流到appserver,同一iP的请求分发到同一个appserver,防止session的分布式问题。
第四种想法:
不用session而是自己定义新的cookie来实现(如user_id) 客户端存状态,服务器端可以把数据放到如memcached、mysql内存表等中;
Cookie Based或者用session数据统一放到一个redis里面
第五种想法:
使用Session Sticky或者Session Replication
自己写的解决局部问题的方式(Springmvc+Redis配置好的--maven管理的)
ApplicationContext applicationContext=(ApplicationContext) req.getSession().getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT"); 
RedisTemplate redisTemplate =(RedisTemplate)applicationContext.getBean("redisTemplate");
如果多处用到了session而且为了减少最少的改动且提高复用性请往下看
相关环境配置
1首先,我们需要引入基本的jar包。maven中的基本引用如下:

  <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.4.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.6.2</version>
        </dependency>
 2配置下springmvc.xml

2-1配置jedisPoolConfig

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">

        <property name="maxTotal" value="${redis.pool.maxTotal}" /><!-- 
            maxActive -->
        <property name="maxIdle" value="${redis.pool.maxIdle}" />
        <property name="minIdle" value="${redis.pool.minIdle}" />
        <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
    </bean>
2-2配置jedisConnectionFactory
<bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}" />
        <property name="port" value="${redis.port}" />
        <property name="timeout" value="${redis.timeout}" />
        <property name="usePool" value="${redis.usePool}" />
        <property name="poolConfig" ref="jedisPoolConfig" />
    </bean>
2-3配置stringRedisSerializer
                 <bean id="stringRedisSerializer"class="org.springframework.data.redis.serializer.StringRedisSerializer" />       2-4配置缓存机制
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory"/>
        <property name="keySerializer" ref="stringRedisSerializer"/>
        <property name="hashKeySerializer" ref="stringRedisSerializer"/></bean>                        
3、redis.properties
#\u6d4b\u8bd5
redis.host=172.30.9.12
#redis\u7aef\u53e3
redis.port=6908
#redis\u8fde\u63a5\u6c60\u6700\u5927\u503c
redis.pool.maxTotal=300
#redis\u8fde\u63a5\u6700\u5927\u7a7a\u95f2\u503c
redis.pool.maxIdle=20
#redis\u8fde\u63a5\u6700\u5c0f\u7a7a\u95f2\u503c
redis.pool.minIdle=5
#redis\u83b7\u53d6\u8fde\u63a5\u65f6\u662f\u5426\u9a8c\u8bc1\u53ef\u7528\u6027
redis.pool.testOnBorrow=true
#redis\u8fde\u63a5\u8d85\u65f6\u65f6\u95f4
redis.timeout=5000
#\u662f\u5426\u4f7f\u7528\u8fde\u63a5\u6c60\u7ba1\u7406\u8fde\u63a5
redis.usePool=true
在web.xml中配置(最终达到高效性,复用性、推荐)将所有的session数据都统一放入redis缓存中,依然照常可以使用session
  <filter>
    <filter-name>SessionFilter</filter-name>
    <filter-class>com.msok.insure.utils.RedisSessionFilter</filter-class>
    <init-param>
      <param-name>sessionId</param-name>
    <!-- cookie name, cookie value 是真实的sessionId -->
      <param-value>insure_platform_sid</param-value>
    </init-param>
    <init-param>
      <param-name>cookieDomain</param-name>
      <param-value></param-value>
    </init-param>
    <init-param>
      <param-name>cookiePath</param-name>
      <param-value>/</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>SessionFilter</filter-name>
    <url-pattern>/*</url-pattern>
<!--     <url-pattern>*.shtml</url-pattern> -->
  </filter-mapping>

-------------------------------------------------配置的sessionFilter类 代码如下---------------------------------
package com.msok.insure.utils;


import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;

/**
 * Session数据存到Redis
 * 
 */
public class RedisSessionFilter implements Filter {

    private String sessionId = "msok_sid";

    private String cookieDomain = "";

    private String cookiePath = "/";

    private int timeoutMinute = 30;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.sessionId = filterConfig.getInitParameter("sessionId");
        this.cookieDomain = filterConfig.getInitParameter("cookieDomain");
        if (this.cookieDomain == null) {
            this.cookieDomain = "";
        }

        this.cookiePath = filterConfig.getInitParameter("cookiePath");
        if (this.cookiePath == null || this.cookiePath.length() == 0) {
            this.cookiePath = "/";
        }
        String timeoutMinute = filterConfig.getInitParameter("timeoutMinute");
        if(timeoutMinute != null && !timeoutMinute.isEmpty()){
            try{
                int timeTmp = Integer.parseInt(timeoutMinute);
                if(timeTmp > 0){
                    this.timeoutMinute = timeTmp;
                }
            } catch(Exception e){

            }
        }
    }

    @Override
    public void doFilter(ServletRequest servletRequest,
            ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 清除url链接上的jsessionid
        if (request.isRequestedSessionIdFromURL()) {
            HttpSession session = request.getSession();
            if (session != null)
                session.invalidate();
        }

        Cookie cookies[] = request.getCookies();
        Cookie sCookie = null;

        String sid = "";
        if (cookies != null && cookies.length > 0) {
            for (int i = 0; i < cookies.length; i++) {
                sCookie = cookies[i];
                if (sCookie.getName().equals(sessionId)) {
                    sid = sCookie.getValue();
                }
            }
        }

        if (sid == null || sid.length() == 0) {
            sid = java.util.UUID.randomUUID().toString();
            Cookie mycookies = new Cookie(sessionId, sid);
            mycookies.setMaxAge(-1);
//            mycookies.setHttpOnly(true);
            if (this.cookieDomain != null && this.cookieDomain.length() > 0) {
                mycookies.setDomain(this.cookieDomain);
            }
            mycookies.setPath(this.cookiePath);
            response.addCookie(mycookies);
        }

        // 去除url中的jsessionid
        HttpServletResponseWrapper wrappedResponse = new HttpServletResponseWrapper(
                response) {
            @Override
            public String encodeRedirectUrl(String url) {
                return url;
            }

            @Override
            public String encodeRedirectURL(String url) {
                return url;
            }

            @Override
            public String encodeUrl(String url) {
                return url;
            }

            @Override
            public String encodeURL(String url) {
                return url;
            }
        };

        filterChain.doFilter(new HttpServletRequestRedisWrapper(sid, request,timeoutMinute),
                wrappedResponse);
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}

这样就再也不用担心单机共享session问题了。嘿嘿。
0 0
原创粉丝点击