Redis实现分布式session功能的共享

最近项目设计集群,实现了一下session的共享功能,其原理是将session保存到分布式缓存数据库中如:redis, memcache等,然后多个服务器tomcat
每次请求都通过NoSql数据库查询,如果存在,则获取值;反之存放值。
我是通过redis来实现session的共享,其主要有一下两种方法:
1、通过tomcat服务器的拓展功能实现
    这种方式比较简单,主要是通过继承session的ManagerBase类,实现重写session相关的方法,这种比较简单,
    参考源码链接(http://download.csdn.net/detail/fengshizty/9417242)。
2、通过filter拦截request请求实现
    下面主要介绍这样实现方式:
    (1)写HttpSessionWrapper实现HttpSession接口,实现里面session相关的方法。
    (2)写HttpServletRequestWrapper继承javax.servlet.http.HttpServletRequestWrapper类,重写对于session  相关的方法。
    (3)写SessionFilter拦截配置的请求url,过去cookie中
    的sessionId,如果为空,对此次请求重写生成一个新的sessionId,在sessionId构造新的HttpServletRequestWrapper对象。
    (4)写SessionService实现session到redis的保存和过去,其key为sessionId,value为session对于的Map。
3、代码实现
    (1)HttpSessionWrapper
/**
 * 创建时间:2016年1月21日 下午7:55:41
 * 
 * @author andy
 * @version 2.2
 */

public class HttpSessionWrapper implements HttpSession {

    private String sid = "";

    private HttpSession session;

    private HttpServletRequest request;

    private HttpServletResponse response;

    private Map<String, Object> map = null;

    private SessionService sessionService = (SessionService) SpringContextHolder.getBean("sessionService");

    public HttpSessionWrapper() {
    }

    public HttpSessionWrapper(HttpSession session) {
        this.session = session;
    }

    public HttpSessionWrapper(String sid, HttpSession session) {
        this(session);
        this.sid = sid;
    }

    public HttpSessionWrapper(String sid, HttpSession session,
            HttpServletRequest request, HttpServletResponse response) {
        this(sid, session);
        this.request = request;
        this.response = response;
    }

    private Map<String, Object> getSessionMap() {
        if (this.map == null) {
            this.map = sessionService.getSession(this.sid);
        }
        return this.map;
    }

    @Override
    public Object getAttribute(String name) {
        if (this.getSessionMap() != null) {
            Object value = this.getSessionMap().get(name);
            return value;
        }
        return null;
    }

    @Override
    public void setAttribute(String name, Object value) {
        this.getSessionMap().put(name, value);
        sessionService.saveSession(this.sid, this.getSessionMap());
    }

    @Override
    public void invalidate() {
        this.getSessionMap().clear();
        sessionService.removeSession(this.sid);
        CookieUtil.removeCookieValue(this.request,this.response, GlobalConstant.JSESSIONID);
    }

    @Override
    public void removeAttribute(String name) {
        this.getSessionMap().remove(name);
        sessionService.saveSession(this.sid, this.getSessionMap()); 
    }

    @Override
    public Object getValue(String name) {
        return this.session.getValue(name);
    }

    @SuppressWarnings("unchecked")
    @Override
    public Enumeration getAttributeNames() {
        return (new Enumerator(this.getSessionMap().keySet(), true));
    }

    @Override
    public String[] getValueNames() {
        return this.session.getValueNames();
    }

    @Override
    public void putValue(String name, Object value) {
        this.session.putValue(name, value);
    }

    @Override
    public void removeValue(String name) {
        this.session.removeValue(name);
    }

    @Override
    public long getCreationTime() {
        return this.session.getCreationTime();
    }

    @Override
    public String getId() {
        return this.sid;
    }

    @Override
    public long getLastAccessedTime() {
        return this.session.getLastAccessedTime();
    }

    @Override
    public ServletContext getServletContext() {
        return this.session.getServletContext();
    }

    @Override
    public void setMaxInactiveInterval(int interval) {
        this.session.setMaxInactiveInterval(interval);
    }

    @Override
    public int getMaxInactiveInterval() {
        return this.session.getMaxInactiveInterval();
    }

    @Override
    public HttpSessionContext getSessionContext() {
        return this.session.getSessionContext();
    }

    @Override
    public boolean isNew() {
        return this.session.isNew();
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
(2)HttpServletRequestWrapper实现
/**
 * 创建时间:2016年1月22日 下午7:52:29
 * 
 * @author andy
 * @version 2.2
 */

public class HttpServletRequestWrapper extends
        javax.servlet.http.HttpServletRequestWrapper {

    private HttpSession session;

    private HttpServletRequest request;

    private HttpServletResponse response;

    private String sid = "";

    public HttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    public HttpServletRequestWrapper(String sid, HttpServletRequest request) {
        super(request);
        this.sid = sid;
    }

    public HttpServletRequestWrapper(String sid, HttpServletRequest request,
            HttpServletResponse response) {
        super(request);
        this.request = request;
        this.response = response;
        this.sid = sid;
        if (this.session == null) {
            this.session = new HttpSessionWrapper(sid, super.getSession(false),
                    request, response);
        }
    }

    @Override
    public HttpSession getSession(boolean create) {
        if (this.session == null) {
            if (create) {
                this.session = new HttpSessionWrapper(this.sid,
                        super.getSession(create), this.request, this.response);
                return this.session;
            } else {
                return null;
            }
        }
        return this.session;
    }

    @Override
    public HttpSession getSession() {
        if (this.session == null) {
            this.session = new HttpSessionWrapper(this.sid, super.getSession(),
                    this.request, this.response);
        }
        return this.session;
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
(3)SessionFilter拦截器的实现
public class SessionFilter extends OncePerRequestFilter implements Filter {

    private static final Logger LOG = Logger.getLogger(SessionFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        //从cookie中获取sessionId,如果此次请求没有sessionId,重写为这次请求设置一个sessionId
        String sid = CookieUtil.getCookieValue(request, GlobalConstant.JSESSIONID);
        if(StringUtils.isEmpty(sid) || sid.length() != 36){
            sid = StringUtil.getUuid();
            CookieUtil.setCookie(request, response, GlobalConstant.JSESSIONID, sid, 60 * 60); 
        }

        //交给自定义的HttpServletRequestWrapper处理
        filterChain.doFilter(new HttpServletRequestWrapper(sid, request, response), response);
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
(4)SessionService实现session从redis的读写存储
public class SessionService {

    private final static Logger LOG = Logger.getLogger(SessionService.class);

    private JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer();

    @Autowired
    private RedisTemplate<Serializable, Serializable> redisTemplate;


    @SuppressWarnings("unchecked")
    public Map<String, Object> getSession(String sid) {
        Map<String, Object> session = new HashMap<String, Object>();
        try {
            Object obj = redisTemplate.opsForValue()
                    .get(RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid);
            if(obj != null){
                obj = jdkSerializer.deserialize((byte[])obj);
                session = (Map<String, Object>) obj;
            }
        } catch (Exception e) {
            LOG.error("Redis获取session异常" + e.getMessage(), e.getCause());
        }

        return session;
    }

    public void saveSession(String sid, Map<String, Object> session) {
        try {
            redisTemplates.opsForValue()
                    .set(RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid,
                            jdkSerializer.serialize(session), RedisKeyUtil.SESSION_TIMEOUT,
                            TimeUnit.MINUTES);
        } catch (Exception e) {
            LOG.error("Redis保存session异常" + e.getMessage(), e.getCause());
        }
    }

    public void removeSession(String sid) {
        try {
            redisTemplates.delete(
                    RedisKeyUtil.SESSION_DISTRIBUTED_SESSIONID + sid);
        } catch (Exception e) {
            LOG.error("Redis删除session异常" + e.getMessage(), e.getCause());
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
(5)Session的拦截配置,一般的我们只需要拦截我们定义的拦截请求拦截,而不需要所有的都需要拦截。在web.xml中配置SessionFilter。
<filter>
        <filter-name>sessionFilter</filter-name>
        <filter-class>org.andy.shop.session.SessionFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>sessionFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

附:设计到的工具类

1、StringUtil工具类

/**
 * String工具类
 * 
 * @author andy
 * @date 2015-5-16 下午4:04:22
 * 
 */
public class StringUtil {

    private StringUtil() {
        super();
    }

    /**
     * 出去null和""
     * @param src
     * @return
     */
    public static String formatNull(String src) {
        return (src == null || "null".equals(src)) ? "" : src;
    }

    /**
     * 判断字符串是否为空的正则表达式,空白字符对应的unicode编码
     */
    private static final String EMPTY_REGEX = "[\\s\\u00a0\\u2007\\u202f\\u0009-\\u000d\\u001c-\\u001f]+";

    /**
     * 验证字符串是否为空
     * 
     * @param input
     * @return
     */
    public static boolean isEmpty(String input) {
        return input == null || input.equals("") || input.matches(EMPTY_REGEX);
    }

    public static boolean isNotEmpty(String input){
        return !isEmpty(input);
    }

    public static String getUuid() {
        return UUID.randomUUID().toString();
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

2、Cookie管理CookieUtil工具类

/**
 * 创建时间:2016年1月22日 下午8:33:56
 * 
 * @author andy
 * @version 2.2
 */

public class CookieUtil {
    private static final String KEY = "jkdflsffff()kldkjapfdY=::$B+DUOWAN";

    private HttpServletRequest request;

    private HttpServletResponse response;

    private static String domain = "andy.com";

    public CookieUtil(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }

    /**
     * 保存cookie
     * @param request
     * @param response
     * @param name cookie名称
     * @param value cookie值
     * @param seconds 过期时间(单位秒) -1代表关闭浏览器时cookie即过期
     */
    public static void setCookie(HttpServletRequest request,
            HttpServletResponse response, String name, String value, int seconds) {
        if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value))
            return;
        Cookie cookie = new Cookie(name, value);
        //cookie.setDomain(domain);
        cookie.setMaxAge(seconds); 
        cookie.setPath("/");
        response.setHeader("P3P",
                "CP='IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT'");
        response.addCookie(cookie);
    }

    /**
     * 过去cookie中保存的值
     * @param name
     * @return
     * @throws UnsupportedEncodingException
     */
    public String getCookieValue(String name)
            throws UnsupportedEncodingException {
        Cookie cookies[] = request.getCookies();
        if (cookies != null) {
            for (int i = 0; i < cookies.length; i++) {
                if (name.equalsIgnoreCase(cookies[i].getName())) {
                    return cookies[i].getValue();
                }
            }
        }
        return "";
    }

    /**
     * 设置加密的cookie
     * @param name cookie名称
     * @param value cookie值
     * @param seconds 过期时间 -1代表关闭浏览器时cookie即过期
     */
    public void setCheckCodeCookie(String name, String value, int seconds) {
        if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
            return;
        }
        String md5Value = MD5Utils.getMD5(KEY + value);
        Cookie cookie = new Cookie(name, md5Value);
        //cookie.setDomain(domain);
        cookie.setMaxAge(seconds);
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    /**
     * 校验加密的cookie
     * @param name
     * @param value
     * @return
     */
    public boolean checkCodeCookie(String name, String value) {
        if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
            return false;
        }
        boolean result = false;
        String cookieValue = getCookieValue(request, name);
        if (MD5Utils.getMD5(KEY + value).equalsIgnoreCase(
                cookieValue)) {
            result = true;
        }
        return result;
    }

    /**
     * 获取cookie值
     * @param request
     * @param name
     * @return
     */
    public static String getCookieValue(HttpServletRequest request, String name) {
        try {
            Cookie cookies[] = request.getCookies();
            if (cookies != null) {
                for (int i = 0; i < cookies.length; i++) {
                    if (name.equalsIgnoreCase(cookies[i].getName())) {
                        return cookies[i].getValue();
                    }
                }
            }
        } catch (Exception e) {
        }
        return "";
    }

    /**
     * 移除客户端的cookie
     * @param request
     * @param response
     * @param name
     */
    public static void removeCookieValue(HttpServletRequest request,
            HttpServletResponse response, String name) {
        try {
            Cookie cookies[] = request.getCookies();
            if (cookies != null && cookies.length > 0) {
                for (Cookie cookie : cookies) {
                    if (name.equalsIgnoreCase(cookie.getName())) {
                        cookie = new Cookie(name, null);
                        cookie.setMaxAge(0);
                        cookie.setPath("/");
                        //cookie.setDomain(domain);
                        response.addCookie(cookie);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146

3、redis键key设置类RedisKeyUtil

/**
 * 创建时间:2015年9月22日 下午4:51:11
 * 
 * @author andy
 * @version 2.2
 */

public class RedisKeyUtil {

    public static final String SESSION_DISTRIBUTED_SESSIONID = "session:distributed:"; //分布式session sessionid -- sessionvalue

    public static final Integer SESSION_TIMEOUT = 2; //session 失效时间2小时

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4、分布式session常量设置类GlobalConstant

/**
 * 创建时间:2016年1月23日 上午11:16:56
 * 
 * 分布式session常量
 * 
 * @author andy
 * @version 2.2
 */

public class GlobalConstant {

    public static final String USER_SESSION_KEY = "user_session_key";//用户session信息

    public static final String JSESSIONID = "YHMJSESSIONID"; //jsessionid
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

分布式session在redis执行结果: 
redis中session保存结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值