服务集群部署时,如何解决session共享

  当我们对后台服务做集群部署时,必须考虑下session共享,作者想到了三种方式:

  1、nginx负载均衡基于ip_hash,每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题

    upstream backserver {
      ip_hash;
      server 192.168.0.1:8080;
      server 192.168.0.2:8080;
    }

  2、nginx负载均衡基于url_hash,按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效

  3、以上两种方式都存在一个不可忽视的问题,如果某台后端服务器宕机了,这个服务器上的session就没了,因此作者这里使用了spring + shiro + redis

    shiro 做安全管理, redis存储session,改动不大,思路是在session存储时,不使用原cache存储,而是用存到redis服务上, shiro配置如下:

    1)、缓存使用我们配置的redis,因此不使用shiro自有的cacheManager

      

    2)、session存储,使用我们的写的RedisSessionDAO

      

    3)、sessionId保存在Cookie中,Tomcat运行下和默认的区别下

      

    4)、RedisSessionDAO继承AbstractSessionDAO ,作者使用的是xml配置

      

      作者使用的RedisTemplate是spring-data-redis中的,少写一些代码,我们也可以自己用jedis写一个

 

public class RedisSessionDAO extends AbstractSessionDAO {

    private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);

    private RedisTemplate redisTemplate;

    private int expire = 0;

    /**
     * The Redis key prefix for the sessions
     */
    private String keyPrefix = "shiro_redis_session:";

    @Override
    public void update(Session session) throws UnknownSessionException {
        this.saveSession(session);
    }

    /**
     * save session
     *
     * @param session
     * @throws UnknownSessionException
     */
    private void saveSession(Session session) throws UnknownSessionException {
        if (session == null || session.getId() == null) {
            logger.error("session or session id is null");
            return;
        }
        String key = getKey(session.getId());
        session.setTimeout(expire * 1000);
        this.redisTemplate.opsForValue().set(key, session);
        this.redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    @Override
    public void delete(Session session) {
        if (session == null || session.getId() == null) {
            logger.error("session or session id is null");
            return;
        }
        redisTemplate.delete(this.getKey(session.getId()));

    }

    @Override
    public Collection<Session> getActiveSessions() {
        Set<Session> sessions = new HashSet<Session>();

        Set<String> keys = redisTemplate.keys(this.keyPrefix + "*");
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                Session s = (Session) redisTemplate.opsForValue().get(key);
                sessions.add(s);
            }
        }

        return sessions;
    }

    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);
        this.saveSession(session);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        if (sessionId == null) {
            logger.error("session id is null");
            return null;
        }
        Session s = (Session) redisTemplate.opsForValue().get(this.getKey(sessionId));
        return s;
    }

    /**
     * 获得String型的key
     *
     * @return
     */
    private String getKey(Serializable sessionId) {
        return this.keyPrefix + sessionId;
    }

    /**
     * Returns the Redis session keys
     * prefix.
     *
     * @return The prefix
     */
    public String getKeyPrefix() {
        return keyPrefix;
    }

    /**
     * Sets the Redis sessions key
     * prefix.
     *
     * @param keyPrefix The prefix
     */
    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public int getExpire() {
        return expire;
    }

    public void setExpire(int expire) {
        this.expire = expire;
    }
}

 

   5)、这里作者遇到一个巨坑,本地测试、测试环境nginx集群测试均ok,但是生产上使用时,遇到用户登录,权限啥的都有了,但是页面上用户信息没显示出来,通过一步一步打印http请求上的Cookie,总算确定了是使用nginx导致的Cookie丢失,https://server_name 和 https://server_name/web 两种方式中默认的cookie上的sessionID存储路径不一致,根据网上的方案,解决方式是将根目录/ 重新指向/web。但是,我这怎么弄都不行,个人推荐以下方式:

  

location / {
    proxy_pass    http://local_tomcat;
    rewrite    ^/$ /web last;
}

location /web/ {
    proxy_pass          http://local_tomcat/web/;
    proxy_set_header    Host            $host;
    proxy_set_header    X-Real-IP       $remote_addr;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;   
}

 

posted @ 2017-10-10 10:59  YoungNong  阅读(1305)  评论(0编辑  收藏  举报