接下来就是实现session的几个函数了,分别是set、get、remove
先实现set,在context.go添加
package context
//context/context.go
import (
"context"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"myGin/redis"
"strings"
"time"
)
type Context struct {
*gin.Context
}
type HandlerFunc func(*Context)
func (c *Context) Domain() string {
return c.Request.Host[:strings.Index(c.Request.Host, ":")]
}
// Session 返回一个session实例
func (c *Context) Session() *Session {
var session Session
cookie, ok := c.Get("_session")
if !ok {
return nil
}
session = cookie.(Session)
return &session
}
type Session struct {
Cookie string `json:"cookie"`
ExpireTime int64 `json:"expire_time"`
SessionList map[string]interface{} `json:"session_list"`
}
func (s *Session) Set(key string, value interface{}) error {
sessionString, err := redis.Client().Get(context.TODO(), s.Cookie).Result()
if err != nil {
return err
}
var session Session
err = json.Unmarshal([]byte(sessionString), &session)
if err != nil {
return err
}
//设置
session.SessionList[key] = value
sessionStringNew, err := json.Marshal(session)
//计算新的过期时间
e := s.ExpireTime - time.Now().Unix()
if e < 0 {
return errors.New("the session has expired")
}
redis.Client().Set(context.TODO(), s.Cookie, sessionStringNew, time.Duration(e)*time.Second)
return nil
}
复制代码
在index控制器中调用
package controller
//controller/Index.go
import (
"myGin/context"
"myGin/response"
)
func Index(context *context.Context) *response.Response {
context.Session().Set("msg", "php是世界上最好的语言!")
return response.Resp().String(context.Domain())
}
复制代码
在redis中查看
设置成功
接下来是get和remove
func (s *Session) Get(key string) (interface{}, error) {
sessionString, err := redis.Client().Get(context.TODO(), s.Cookie).Result()
if err != nil {
return nil, err
}
var session Session
err = json.Unmarshal([]byte(sessionString), &session)
if err != nil {
return nil, err
}
value, ok := session.SessionList[key]
if ok {
return value, nil
}
return nil, errors.New("not found key :" + key)
}
func (s *Session) Remove(key string) error {
sessionString, err := redis.Client().Get(context.TODO(), s.Cookie).Result()
if err != nil {
return err
}
var session Session
err = json.Unmarshal([]byte(sessionString), &session)
if err != nil {
return err
}
delete(session.SessionList, key)
sessionStringNew, err := json.Marshal(session)
if err != nil {
return err
}
//计算新的过期时间
e := s.ExpireTime - time.Now().Unix()
if e < 0 {
return errors.New("the session has expired")
}
redis.Client().Set(context.TODO(), s.Cookie, sessionStringNew, time.Duration(e)*time.Second)
return nil
}
复制代码
调用
package controller
//controller/Index.go
import (
"myGin/context"
"myGin/response"
)
func Index(context *context.Context) *response.Response {
context.Session().Set("msg", "php是世界上最好的语言!")
return response.Resp().String(context.Domain())
}
func Index2(context *context.Context) *response.Response {
msg, _ := context.Session().Get("msg")
return response.Resp().String(msg.(string))
}
func Index3(context *context.Context) *response.Response {
context.Session().Remove("msg")
return response.Resp().String("")
}
复制代码
到这里,session的功能基本都实现了,下面看一个有意思的问题。
func Index4(context *context.Context) *response.Response {
session := context.Session()
for i := 0; i < 100; i++ {
go func(index int) {
session.Set("msg"+strconv.Itoa(index), index)
}(i)
}
return response.Resp().String("")
}
复制代码
访问这个请求,redis中的结果是这样的
上面的循环执行了100个协程,按理说redis中的数据是应该有100个的,这里却只有2个。
原因在于每个session设置都是并行的,读取的结果并不是最终结果,所以session的设置还需要加一个锁。
完整代码如下
package context
//context/context.go
import (
"context"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"myGin/redis"
"strings"
"sync"
"time"
)
type Context struct {
*gin.Context
}
type HandlerFunc func(*Context)
func (c *Context) Domain() string {
return c.Request.Host[:strings.Index(c.Request.Host, ":")]
}
// Session 返回一个session实例
func (c *Context) Session() *Session {
var session Session
cookie, ok := c.Get("_session")
if !ok {
return nil
}
session = cookie.(Session)
session.Lock = &sync.Mutex{}
return &session
}
type Session struct {
Cookie string `json:"cookie"`
ExpireTime int64 `json:"expire_time"`
SessionList map[string]interface{} `json:"session_list"`
Lock *sync.Mutex
}
func (s *Session) Set(key string, value interface{}) error {
//加锁,防止读取并行
s.Lock.Lock()
defer s.Lock.Unlock()
sessionString, err := redis.Client().Get(context.TODO(), s.Cookie).Result()
if err != nil {
return err
}
var session Session
err = json.Unmarshal([]byte(sessionString), &session)
if err != nil {
return err
}
//设置
session.SessionList[key] = value
sessionStringNew, err := json.Marshal(session)
//计算新的过期时间
e := s.ExpireTime - time.Now().Unix()
if e < 0 {
return errors.New("the session has expired")
}
redis.Client().Set(context.TODO(), s.Cookie, sessionStringNew, time.Duration(e)*time.Second)
return nil
}
func (s *Session) Get(key string) (interface{}, error) {
sessionString, err := redis.Client().Get(context.TODO(), s.Cookie).Result()
if err != nil {
return nil, err
}
var session Session
err = json.Unmarshal([]byte(sessionString), &session)
if err != nil {
return nil, err
}
value, ok := session.SessionList[key]
if ok {
return value, nil
}
return nil, errors.New("not found key :" + key)
}
func (s *Session) Remove(key string) error {
s.Lock.Lock()
defer s.Lock.Unlock()
sessionString, err := redis.Client().Get(context.TODO(), s.Cookie).Result()
if err != nil {
return err
}
var session Session
err = json.Unmarshal([]byte(sessionString), &session)
if err != nil {
return err
}
delete(session.SessionList, key)
sessionStringNew, err := json.Marshal(session)
if err != nil {
return err
}
//计算新的过期时间
e := s.ExpireTime - time.Now().Unix()
if e < 0 {
return errors.New("the session has expired")
}
redis.Client().Set(context.TODO(), s.Cookie, sessionStringNew, time.Duration(e)*time.Second)
return nil
}
复制代码
有疑问加站长微信联系(非本文作者)