妙用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: 这个方法在实践中特别好用,如果你有其他的方法,还请不吝赐教

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,281评论 19 139
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,787评论 2 22
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,007评论 6 342
  • 原文链接:https://docs.spring.io/spring-boot/docs/1.4.x/refere...
    pseudo_niaonao阅读 4,798评论 0 9
  • 周末这两天感觉一直在路上折腾,以前不晕车,现在一坐上汽车,就晕得很。小庆爸妈风尘仆仆从湖北老家过来,这个周末我们要...
    九月星辰阅读 213评论 4 2