【问题标题】:Should I copy session for each operation in mgo?我应该为mgo中的每个操作复制会话吗?
【发布时间】:2015-10-22 07:21:32
【问题描述】:

我想upsert 一个记录列表,所以我有两个选择,一个只使用一个会话,另一个为每个记录复制一个会话。所以,在我看来,第一种方法可能比第二种方法慢,但是第一种方法会导致创建的会话过多吗?

1.使用一个会话

func (this *CvStoreServiceImpl) SetCvJobItemMeasureList(accessToken *base_datatype.ServiceAccessToken, versionPolicy string, jobItemList []*cv_common_type.CvJobItemMeasure) (err error) {
    session := this.session.Clone()
    defer session.Close()

    for _, jobItem := range jobItemList {
        objKey := &orm.ItemIdKey{
            VersionName: versionPolicy, //XXX
            ItemId:      jobItem.ItemId,
        }
        obj := orm.ConvertToCvJobItemMeasureObj(versionPolicy, jobItem)
        _, err2 := this.jobMeasureCollection.With(session).Upsert(objKey, obj)
        if nil != err2 {
            err = &common_error.NamedError{err2.Error()}
            this.logger.Println(err2.Error())
        }
    }
    return
}

2.为每条记录复制会话

func (this *CvStoreServiceImpl) SetCvJobItemMeasure(accessToken *base_datatype.ServiceAccessToken, versionPolicy string, jobItem *cv_common_type.CvJobItemMeasure) (err error) {
    session := this.session.Clone()
    defer session.Close()

    objKey := &orm.ItemIdKey{
        VersionName: versionPolicy, //XXX
        ItemId:      jobItem.ItemId,
    }
    obj := orm.ConvertToCvJobItemMeasureObj(versionPolicy, jobItem)
    _, err2 := this.jobMeasureCollection.With(session).Upsert(objKey, obj)
    if nil != err2 {
        err = &common_error.NamedError{err2.Error()}
        return
    }
    return
}

然后在forloop中调用这个方法:

for _, item := range cvMeasure.GetJobList() {
    err = this.SetCvJobItemMeasure(accessToken, versionPolicy, item)
    if nil != err {
        return
    }
}

【问题讨论】:

标签: session go mgo


【解决方案1】:

首先,我们需要看看mgo.Session.Copy()mgo.Session.Clone()的区别。当go.Session.Clone() 返回一个新会话时,该会话使用相同的套接字连接。这不一定是一件坏事,但请记住,在服务器端,每个连接都会分配一个堆栈。因此会话将共享相同的堆栈。根据您的用例,这可能会产生很大的不同。

这就是问题所在——如果你为每条记录打开一个新的套接字连接,这会导致三次握手,这很慢。重用同一个套接字可以减少这种开销,但仍然存在一些并且具有上述缺点。

我倾向于为每个长时间运行的工作单元建立一个新连接。一个简单的例子说明了这一点:

package main

import (
    "fmt"
    mgo "gopkg.in/mgo.v2"
    bson "gopkg.in/mgo.v2/bson"
    "net/http"
)

var (
    Database *mgo.Database
)


// The listEntries lists all posts
func listPosts(w http.ResponseWriter, r *http.Request) {

    // We have a rather long running unit of work
    // (reading and listing all posts)
    // So it is worth copying the session   
    collection := Database.C("posts").With( Database.Session.Copy() )

    post  := bson.D{}
    posts := collection.Find(bson.M{}).Iter()

    for posts.Next(&post) {
        // Process posts and send it to w
    }

}

func main() {

    session, _ := mgo.Dial("mongodb://localhost:27017")

    Database := session.DB("myDb")

    // Count is a rather fast operation
    // No need to copy the session here
    count, _ := Database.C( "posts" ).Count()

    fmt.Printf("Currently %d posts in the database", count )

    http.HandleFunc("/posts", listPosts)
    http.ListenAndServe(":8080", nil)
}

【讨论】:

  • 我喜欢这种方法。但是,您如何处理闭幕式会议?我看到人们在某些时间点使用 defer 来关闭会话。这真的有必要吗?如果是这样,我们应该如何将其纳入您的示例中?
  • @codepushr Uuups... ;) 随意,否则我稍后再做
  • 我不确定它是如何正确完成的,可能会在 main 和处理程序中推迟 session.Close() 但使用副本?
【解决方案2】:

是的,最好复制一个会话来执行一个或几个操作,让mgo中的连接池来提高性能。一个 mongo 服务器的默认限制是 4096,以防止连接过多。

func newSession(consistency Mode, cluster *mongoCluster, timeout time.Duration) (session *Session) {
    cluster.Acquire()
    session = &Session{
        cluster_:    cluster,
        syncTimeout: timeout,
        sockTimeout: timeout,
        poolLimit:   4096,
    }
    debugf("New session %p on cluster %p", session, cluster)
    session.SetMode(consistency, true)
    session.SetSafe(&Safe{})
    session.queryConfig.prefetch = defaultPrefetch
    return session
}

【讨论】:

  • mgo.Session.Copy() 以更简单的方式做同样的事情,而且它保留了原始会话的身份验证信息和其他配置,并且会话是延迟设置的。
猜你喜欢
  • 2016-06-30
  • 2019-06-04
  • 2021-11-26
  • 1970-01-01
  • 2017-12-11
  • 2018-11-18
  • 1970-01-01
  • 2021-12-01
  • 1970-01-01
相关资源
最近更新 更多