這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。自訂 session 結構體:
type Session struct {SessionID string `json:"sessionId" bson:"sessionId"`User *User `json:"-" bson:"user"`UserType string `json:"userType" bson:"userType"`NickName string `json:"nickName" bson:"nickName"`CreateTime time.Time `json:"-" bson:"createTime"`UpdateTime time.Time `json:"-" bson:"updateTime"`Expires time.Time `json:"-" bson:"expires"`Locale string `json:"-" bson:"locale"` // default is zh_CNMenus []wmodel.Menu `json:"menus" bson:"menus"`}
1. session 的儲存
使用 json.Marshal 將結構體 json 化之後儲存到 redis:
/*【增】描述:插入一個 session 對象session 頂級 key,頂級 key 可以設定到期時間<[session]: 要插入的 session 對象>[error]:插入失敗相關資訊*/func (s *sessionService) SetSession(session *model.Session) error {// 從池裡擷取串連conn := pool.Get()if conn == nil {log.Errorf("redis connnection is nil")return errors.New("redis connnection is nil")}// 用完後將串連放回串連池defer conn.Close()// 將session轉換成json資料,注意:轉換後的value是一個byte數組value, err := json.Marshal(session)if err != nil {log.Errorf("json marshal err,%s", err)return err}log.Infof("send data[%s]", session.SessionID, value)_, err = conn.Do("SET", session.SessionID, value, "EX", sessionTimeOutInSeconds)if err != nil {return err}return nil}
Golang 測實驗證:
func TestSetSession(t *testing.T) {s := &model.Session{SessionID: "20150421120000",UserType: "admin",NickName: "df",}err := SessionService.SetSession(s)if err != nil {t.Errorf("fail to add one session(%+v): %s", s, err)t.FailNow()}}
redis 用戶端查看該 session 儲存情況:
2. session 的刪除
直接通過 redigo 驅動遠程調用 DEL 命令:
/*【刪】描述: 刪除一個 session 對象session 頂級 key,一般情況下 session 會在使用者無操作 30 分鐘後自行到期刪除但使用者登出操作可以提前對 session 進行刪除,這就是本方法被調用的地方<[sessionID]: 要刪除的 session 對象的 id>[error]:刪除失敗相關資訊*/func (s *sessionService) DelSession(sessionID string) (err error) {// 從池裡擷取串連conn := pool.Get()if conn == nil {log.Errorf("redis connnection is nil")return errors.New("redis connnection is nil")}// 用完後將串連放回串連池defer conn.Close()log.Infof("move data[%s]", sessionID)_, err = conn.Do("DEL", sessionID)if err != nil {return err}return nil}
Golang 測實驗證:
func TestDelSession(t *testing.T) {err := SessionService.DelSession("20150421120000")if err != nil {t.Errorf("fail to delete one session(%s): %s", "20150421120000", err)t.FailNow()}}
3. session 的擷取
使用 json.Unmarshal 將序列化之後的結構體還原:
/*【查】描述: 查看並返回一個 session 實體session 頂級 key<[sessionID]: 要查看的 session 對象的 id>[error]:查看失敗相關資訊*/func (s *sessionService) GetSession(sessionID string) (session *model.Session, err error) {// 從池裡擷取串連conn := pool.Get()if conn == nil {log.Errorf("redis connnection is nil")return nil, errors.New("redis connnection is nil")}// 用完後將串連放回串連池defer conn.Close()log.Infof("exists data[%s]", sessionID)// 先查看該session是否存在var ifExists boolifExists, err = SessionService.ExistsSession(sessionID)if err != nil {log.Errorf("fail to exists one session(%s): %s", sessionID, err)return nil, errors.New("session not exists, sessionID: " + sessionID)}if ifExists {// json資料在go中是[]byte類型,所以此處用redis.Bytes轉換valueBytes, err2 := redis.Bytes(conn.Do("GET", sessionID))if err2 != nil {return nil, err2}//log.Infof("receive data[%s]:%s", sessionID, string(valueBytes))session = &model.Session{}err = json.Unmarshal(valueBytes, session)if err != nil {return nil, err}return session, nil} else {return nil, errors.New("session not exists, sessionID: " + sessionID)}}
Golang 測實驗證:
func TestGetSession(t *testing.T) {s, err := SessionService.GetSession("20150421120000")if err != nil {t.Errorf("fail to exists one session(%s): %s", "20150421120000", err)t.FailNow()}log.Debug("session exists, session nickname is: %s", s.NickName)}
後記
redis 的作者為了保持簡單的架構只允許我們對 top level 的 key 設定逾時時間,次級 key(比如 Hash 裡邊的每個子 key)是不能設定逾時時間的,所以我們單獨使用了一個 index 為 3 的 redis 庫專門存放 session,就是方便管理。另外,我們將每個 session 的 key(即 top level 的 key)有效期間設定為半小時,有效期間斷定及處理託管 redis,避開了程式裡對 session 逾時機制管理的複雜性——特別是分布式環境。
另外,本文只提供了 session 的增、刪、查操作,對於 session 的修改以及有效期間推延操作筆者建議可以先刪除再增加。
參考資料
GO: How to save and retrieve a struct to redis using redigo