妙用Spring Request Bean实现分布式会话管理

实现分布式会话的常用模式,是把会话信息集中保存在Redis服务器中,业务服务器实现为负状态模式,方便会话的一致性。利用Spring中的request bean,可以非常优雅地实现会话信息的自动管理。

会话信息管理(LazyRedisSession)

import com.google.common.base.Strings;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import redis.clients.jedis.JedisCommands;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基于Redis的懒加载会话信息(没有与Redis实时保持统统不).
 *
 * @author tenmao
 */
@Slf4j
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Getter
@Component
public class LazyRedisSession {
    private static final String PREFIX = "com.tenmao.redis.";

    @Resource
    private JedisCommands jedisCommands;

    @Resource
    private HttpServletRequest request;

    private String sessionId;

    private Map<String, String> attributes = new ConcurrentHashMap<>();

    @PostConstruct
    private void init() {
        //这里是从HttpServletRequest中获取会话ID,实际项目中,也可以从ThreadLocal或者MDC中获取
        sessionId = request.getHeader("Session-Id");
        log.info("redis session loaded for session [{}]", sessionId);
        if (!Strings.isNullOrEmpty(sessionId)) {
            final Map<String, String> attributesInRedis = jedisCommands.hgetAll(PREFIX + sessionId);
            attributes.putAll(attributesInRedis);
        }
    }

    @PreDestroy
    private void close() {
        log.info("redis session saved to redis session [{}]", sessionId);
        if (!Strings.isNullOrEmpty(sessionId)) {
            jedisCommands.del(PREFIX + sessionId);
            if (!attributes.isEmpty()) {
                jedisCommands.hmset(PREFIX + sessionId, attributes);
            }
        }
    }

    /**
     * Binds an object to this session, using the name specified.
     * If an object of the same name is already bound to the session,
     * the object is replaced.
     *
     * @param name  the name to which the object is bound;
     *              cannot be null
     * @param value the string to be bound
     */
    public void setAttribute(String name, String value) {
        attributes.put(name, value);
    }

    /**
     * Returns the object bound with the specified name in this session, or
     * <code>null</code> if no object is bound under the name.
     *
     * @param name a string specifying the name of the object
     * @return the string with the specified name
     */
    public String getAttribute(String name) {
        return attributes.get(name);
    }
}

使用

使用方式特别简单,就把LazeRedisSession当做普通的bean注入到其他Bean中就可以了

@RestController
@RequestMapping("test")
public class TestController {
    @Resource
    private LazyRedisSession lazyRedisSession;
    @RequestMapping(value = "/ping", method = RequestMethod.GET)
    public String ping(@RequestParam String name) {
        final String lastName = lazyRedisSession.getAttribute("name");
        lazyRedisSession.setAttribute("name", name);
        return lastName;
    }
}

ps: 这个方法在实践中特别好用,如果你有其他的方法,还请不吝赐教

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券