在mongodb golang中保存会话

时间:2016-09-22 09:00:52

标签: mongodb go mgo

我在后端使用golang而mongodb是我的数据库。 我希望将我的web应用程序的用户会话(登录和注销)存储在mongodb中以用于持久性。因为有提供程序仅适用于mysql而不是mongodb,我编辑它以支持mongodb.But当我尝试使用它时我得到无效的内存地址或无指针取消引用。 代码如下,如果有更好的编码方式,请告诉我。谢谢

type (
    SessionStore struct {
        c      *mgo.Session
        sid    string
        lock   sync.RWMutex
        values map[interface{}]interface{}
    }
)

var mgopder = &Provider{}

func (st *SessionStore) Set(key, value interface{}) error {
    st.lock.Lock()
    defer st.lock.Unlock()
    st.values[key] = value
    return nil
}

// Get value from mongodb session
func (st *SessionStore) Get(key interface{}) interface{} {
    st.lock.RLock()
    defer st.lock.RUnlock()
    if v, ok := st.values[key]; ok {
        return v
    }
    return nil
}

// Delete value in mongodb session
func (st *SessionStore) Delete(key interface{}) error {
    st.lock.Lock()
    defer st.lock.Unlock()
    delete(st.values, key)
    return nil
}

// Flush clear all values in mongodb session
func (st *SessionStore) Flush() error {
    st.lock.Lock()
    defer st.lock.Unlock()
    st.values = make(map[interface{}]interface{})
    return nil
}

// SessionID get session id of this mongodb session store
func (st *SessionStore) SessionID() string {
    return st.sid
}

// SessionRelease save mongodb session values to database.
// must call this method to save values to database.
func (st *SessionStore) SessionRelease(w http.ResponseWriter) {
    defer st.c.Close()
    b, err := session.EncodeGob(st.values)
    if err != nil {
        return
    }
    st.c.DB("Employee").C("Sessions").Update(nil, bson.M{"$set": bson.M{
        "session_data":   b,
        "session_expiry": time.Now().Unix(),
        "session_key":    st.sid,
    },
    },
    )

    /*st.c.Exec("UPDATE "+TableName+" set `session_data`=?, `session_expiry`=? where session_key=?",
    b, time.Now().Unix(), st.sid)*/
}

type Provider struct {
    maxlifetime int64
    savePath    string
    Database    string
}

// connect to mongodb
func (mp *Provider) connectInit() *mgo.Session {
    ds, err := mgo.Dial("Employee")
    if err != nil {
        return nil
    }
    return ds
}

// SessionInit init mongodb session.
// savepath is the connection string of mongodb
func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error {
    mp.maxlifetime = maxlifetime
    mp.savePath = savePath
    mp.Database = "Employee"
    return nil
}

// SessionRead get mysql session by sid
func (mp *Provider) SessionRead(sid string) (session.Store, error) {
    var sessiondata []byte
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB(mp.Database).C("Session")
    err := c.Find(bson.M{
        "session_key": sid,
    }).Select(bson.M{"session_data": 1}).All(&sessiondata)
    if err != nil {
        if err.Error() == "not found" {
            c.Insert(bson.M{
                "session_key":    sid,
                "session_data":   " ",
                "session_expiry": time.Now().Unix(),
            })
        }
    }

    var kv map[interface{}]interface{}
    if len(sessiondata) == 0 {
        kv = make(map[interface{}]interface{})
    } else {
        kv, err = session.DecodeGob(sessiondata)
        if err != nil {
            return nil, err
        }
    }
    rs := &SessionStore{c: ds, sid: sid, values: kv}
    return rs, nil
}

// SessionExist check mongodb session exist
func (mp *Provider) SessionExist(sid string) bool {
    var sessiondata []byte
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    err := c.Find(bson.M{
        "session_key": sid,
    }).Select(bson.M{
        "session_data": 1,
    }).One(&sessiondata)
    if err != nil {
        if err.Error() == "not found" {
            return false
        }
    }
    return true

}

// SessionRegenerate generate new sid for mysql session
func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
    var sessiondata []byte
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    err := c.Find(bson.M{
        "session_key": oldsid,
    }).Select(bson.M{
        "session_data": 1,
    }).One(&sessiondata)
    if err != nil {
        if err.Error() == "not found" {
            c.Insert(bson.M{
                "sessoin_key":    oldsid,
                "session_data":   " ",
                "session_expiry": time.Now().Unix(),
            })
        }
    }
    /*  row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid)

        err := row.Scan(&sessiondata)

    c.Update(bson.M{"sessoin_key": oldsid}, bson.M{
        "$set": bson.M{
            "session_key": sid,
        },
    })
    /*c.Exec("update "+TableName+" set `session_key`=? where session_key=?", sid, oldsid)
     */
    var kv map[interface{}]interface{}
    if len(sessiondata) == 0 {
        kv = make(map[interface{}]interface{})
    } else {
        kv, err = session.DecodeGob(sessiondata)
        if err != nil {
            return nil, err
        }
    }
    rs := &SessionStore{c: ds, sid: sid, values: kv}
    return rs, nil
}

// SessionDestroy delete mysql session by sid
func (mp *Provider) SessionDestroy(sid string) error {
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    c.Remove(bson.M{
        "session_key": sid,
    }) 
    return nil
}

// SessionGC delete expired values in mysql session
func (mp *Provider) SessionGC() {
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    c.Remove(bson.M{
        "session_expiry": bson.M{
            "$lt": time.Now().Unix() - mp.maxlifetime,
        },
    })
return
}

// SessionAll count values in mysql session
func (mp *Provider) SessionAll() int {
    var total int
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    total, err := c.Count()

    if err != nil {
        return 0
    }
    return total
}

func init() {
    session.Register("mongodb", mgopder)
}

错误:

panic: runtime error: invalid memory address or nil pointer dereference
        panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x6db254]

goroutine 6 [running]:
panic(0xa2f560, 0xc0820080b0)
        C:/Go/src/runtime/panic.go:481 +0x3f4
gopkg.in/mgo%2ev2.(*Session).Close(0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:1612 +0x144
panic(0xa2f560, 0xc0820080b0)
        C:/Go/src/runtime/panic.go:443 +0x4f7
gopkg.in/mgo%2ev2.(*Session).acquireSocket(0x0, 0xc082290000, 0x0, 0x0, 0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:4409 +0x4ba
gopkg.in/mgo%2ev2.(*Collection).writeOp(0xc082279f30, 0x8feb80, 0xc082326060, 0xc082326001, 0x0, 0x0, 0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:4604 +0xe7
gopkg.in/mgo%2ev2.(*Collection).Remove(0xc082279f30, 0x9d4700, 0xc082326030, 0x0, 0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:2586 +0x15c
sample/models.(*Provider).SessionGC(0xe2f5a0)
        C:/Projects/Go/src/sample/models/model.go:234 +0x3dc
github.com/astaxie/beego/session.(*Manager).GC(0xc082258b20)
        C:/Projects/Go/src/github.com/astaxie/beego/session/session.go:271 +0x48
created by github.com/astaxie/beego.registerSession
        C:/Projects/Go/src/github.com/astaxie/beego/hooks.go:68 +0x31d

2 个答案:

答案 0 :(得分:1)

这就是我通常做的事情。

package mongo

import (
    "time"

    "gopkg.in/mgo.v2"
)

// DataStore containing a pointer to a mgo session
type DataStore struct {
    Session *mgo.Session
}

// ConnectToTagserver is a helper method that connections to pubgears' tagserver
// database
func (ds *DataStore) ConnectToTagserver() {
    mongoDBDialInfo := &mgo.DialInfo{
        Addrs:    []string{"some IP"},
        Timeout:  60 * time.Second,
        Database: "some db",
    }
    sess, err := mgo.DialWithInfo(mongoDBDialInfo)
    if err != nil {
        panic(err)
    }
    sess.SetMode(mgo.Monotonic, true)
    ds.Session = sess
}

// Close is a helper method that ensures the session is properly terminated
func (ds *DataStore) Close() {
    ds.Session.Close()
}

然后在我的模型包中我做了类似的事情

package models

import (
    "./mongo"
    "gopkg.in/mgo.v2/bson"
)

// AdSize represents the data object stucture that is returned by querying
// mongo's account collection
type AdSize struct {
    ID        bson.ObjectId `bson:"_id,omitempty"`
    Providers []string      `bson:"providers"`
    Size      string        `bson:"size"`
}

// GetAllAdsizes is a helper function designed to retrieve all the objects in the
// adsize collection
func GetAllAdsizes() ([]AdSize, error) {
    ds := mongo.DataStore{}
    ds.ConnectToTagserver()
    defer ds.Close()
    adSizes := []AdSize{}
    adSizeCollection := ds.Session.DB("some database").C("some collection")
    err := adSizeCollection.Find(bson.M{}).Sort("name").All(&adSizes)
    return adSizes, err
}

所以我在mongo文件中创建了一个会话包装器,然后在models文件中创建了一个会话对象,最后在一些路由文件中我调用了GetAllAdsizes()方法,该方法处理了我的mongo会话。会话保持活动直到GetAllAdsizes()方法结束,因为它在延迟时关闭。但是,可以修改这样的内容,您可以处理所有用户的内容,然后在用户注销时关闭会话。还可以看看Best practice to maintain a mgo session,在那里你可以看到类似的逻辑。

答案 1 :(得分:0)

不确定您的代码,下面是一个包文件示例,该文件在go中创建了一个mongodb会话并使用它。

package main

import (
    "fmt"
    "os"

    "mydb"
)

func main() {
    // <user> the user to access the db
    // <pwd> the database user password
    // <host> hostname or ip address of the database server (localhost i.e.)
    // <port> the port where the database server is listening (usually 27016).
    mydb.NewDbSession("mongodb://<user>:<pwd>@<host>:<port>/?authSource=<db_name>")

    session := mydb.Db.Copy()
    defer session.Close()

    col := session.DB("my_db_schema").C("my_collection_name")

    // row is a list of struct depends on collection
    err := col.Find(bson.M{"field_name": "value"}).All(&row)
    if err != nil {
        fmt.Printf("%s\n", err)
    }

}

然后在你的主要包裹中:

type SessionStore struct {
   c      *mgo.Session
   sid    string
   lock   sync.RWMutex
   values map[interface{}]interface{}
}

这是一个非常简单的例子,可以帮助你开始。

考虑到MongoDB与MySQL非常不同,因为它是 NoSQL 架构数据库管理系统。

有关详细信息,请参阅 mgo 包的文档。

您的用户会话结构存在一些问题:

type SessionStore struct {
   C      *mgo.Session                `json:"C" bson:"c"`
   Sid    string                      `json:"Sid" bson:"sid,omitempty"`
   Lock   sync.RWMutex                `json:"Lock" bson:"lock,omitempty"`
   Values map[interface{}]interface{} `json:"Values" bson:"values,omitempty"`
}

首先,您必须使用大写字母来导出密钥。

例如,您应该使用 C 代替 c ,依此类推 Sid Lock 和< EM>值

ArrayMap

我认为这不会适用于指针。