Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

可用性问题 #12

Closed
lipengming opened this issue Nov 16, 2015 · 1 comment
Closed

可用性问题 #12

lipengming opened this issue Nov 16, 2015 · 1 comment

Comments

@lipengming
Copy link

RedisSessionDao完全依赖于redis,如果redis挂掉了,或者本来redis就没有启动,那么该组件会导致项目全部不可用。所以考虑一下可用性。

1、确保单机一定可用:

private static Logger logger = LoggerFactory.getLogger(SessionCache.class);
private String keyPrefix = "shiro_redis_session:";

private final CacheClusterClient cacheClusterClient;
private final int timeOut ;

public SessionCache(CacheClusterClient cacheClusterClient, int timeOut) {
    this.cacheClusterClient = cacheClusterClient;
    this.timeOut = timeOut;
}

/**
 * 读取session
 *
 * @param sessionId
 * @return
 */
public Session readSession(Serializable sessionId) {
    try {
        return sessionCache.get(sessionId);
    } catch (Exception e) {
        return null;
    }
}

/**
 * 保存session
 *
 * @param session
 */
public void saveSession(Session session) {
    if(session == null || session.getId() == null){
        logger.error("session or session id is null");
        return;
    }
    byte[] key = getKey(session.getId());
    byte[] value = SerializeUtil.serialize(session);
    session.setTimeout(timeOut * 1000);
    try{
        cacheClusterClient.set(key, value);
        cacheClusterClient.expire(key,timeOut);
    } catch (Exception e) {
        logger.error("###########Redis Connect Failed###########", e);
        //save to local
        sessionCache.put(session.getId(),session);
    }
}

/**
 * 作废session
 *
 * @param sessionId
 */
public void invalid(Serializable sessionId) {
    sessionCache.invalidate(sessionId);
    try{
        cacheClusterClient.del(getKey(sessionId));
    } catch (Exception e) {
        logger.error("###########Redis Connect Failed###########", e);
    }
}

/**
 * 所有活动的session
 *
 * @return
 */
public Collection<Session> getActive() {
    return sessionCache.asMap().values();
}

/////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
/////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
/////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

private LoadingCache<Serializable,Session> sessionCache = CacheBuilder.newBuilder()
        .maximumSize(100)//设置缓存最大容量为100
        .expireAfterWrite(10, TimeUnit.MINUTES)//设置写缓存后30分钟过期,过期后会触发removalListener
        .concurrencyLevel(4)//并发级别为4,并发级别是指可以同时写缓存的线程数
        .removalListener(new RemovalListener<Object, Object>() {//这段代码可以移除
            @Override
            public void onRemoval(RemovalNotification<Object, Object> notification) {
                logger.debug("session was removed with key" + notification.getKey() + " cause is :" + notification.getCause());
            }
        })
        .build(new CacheLoader<Serializable,Session>() {//如果缓存中根据Key没有取到值,那么执行这里面的操作了
            @Override
            public Session load(Serializable key) throws Exception {
                return readRedis(key);//当返回null的时候会抛异常,CacheLoader.InvalidCacheLoadException
            }
        });


private byte[] getKey(Serializable sessionId){
    String preKey = this.keyPrefix + sessionId;
    return SerializeUtil.serialize(preKey);
}

private Session readRedis(Serializable sessionId) {
    if(sessionId == null){
        logger.error("session id is null");
        return null;
    }
    try {
        byte[] key = getKey(sessionId);
        byte[] value = cacheClusterClient.get(key);
        if(value == null) return null;
        Session s = (Session) SerializeUtil.deserialize(value);
        return s;
    } catch (Exception e) {
        logger.error("Redis Connect Failed! Read Err!",e);
    }
    return null;
}

2、提升redis组件本身的可用性:

这个可以 redis的一些cluster的方案,这里不做衍生。

@alexxiyang
Copy link
Owner

建议使用Redis sentinel或者Redis cluster来解决可用性问题,保持Shiro-redis的简单,简单的代码比较robust. 也容易阅读

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants