SETEX

上周遇到了一个非常奇怪的问题,我们组维护的容器化平台的CI组件用的是Harbor做public/private镜像仓库,HA做了alive/alive,上层通过LVS做Load Balence, 两台计算节点间使用redis共享session。这是一套新搭建的环境,上线前只是简单测了测,当时没有发现大问题。但是这周新系统上线之后组里同事反馈说Harbor API调用偶尔会出现401错误,很常见的未授权错误。

于是乎找了harborclient的Python库谢了个小脚本来复现这个错误。这个问题通常是在client一段时候没有call Harbor API之后再调用时出现的,而且只出现一次,很少会有连续报错的情况。

接下来就是查log了,tail -f调出两台server的log观察,发现原来如果/login(get a token from administrator)和/get/post(request with the privious token)的两个请求不打到同一台机器上的话就会100%复现这个问题,问题解决思路这下就非常清楚了,八成就是session共享没有成功。

单步调试的话GDB是个很不错的工具,但是为了更直观的观察这个问题,我动手改了harborclient.py依赖库,在每步请求时打出当前请求带着的session ID,然后进一步拿着复现出来的两个session ID去redis里面查session是否存在以及session值。

调试结果是两台机器都能正常读写session,但是session并不能共享,而且第二次请求的session values为空,缺查询关键字userid。我看了一下源码,当/logout的时候,每次路由的时候都会先调用GlobalSessions.SessionStart(w, r)获取session.SessionStore,然后defer session.SessionStore.SessionRelease(w),sess_redis.go的实现:

func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
    c := rs.p.Get()
    defer c.Close()
    b, err := session.EncodeGob(rs.values)
    if err != nil {
        return
    }
    c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
}

SessionRelease函数最后执行的SETEX命令是将value关联到key,并将key的ttl设为seconds(second), 如果key已经存在,SETEX将覆写旧值。

这个命令类似于以下两个命令:

SET key value
EXPIRE key seconds

不同之处是, SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,设置成功时返回OK, 当seconds参数不合法时,返回错误ERR invalid expire time in setex.

我来评几句
登录后评论

已发表评论数()