Mongodb 从中学开始读mgo。单调

Mongodb 从中学开始读mgo。单调,mongodb,go,mgo,martini,Mongodb,Go,Mgo,Martini,我正在尝试配置从mongo副本集的主节点和两个辅助节点的读取,以提供更好的负载平衡。3个节点中的每一个都位于具有IP地址的不同计算机上:ip1、ip2、ip3 我的GoLang网站,这是martiniweb服务器,有两个URL/insert和/get: package main import ( "github.com/go-martini/martini" "gopkg.in/mgo.v2" "gopkg.in/mgo

我正在尝试配置从mongo副本集的主节点和两个辅助节点的读取,以提供更好的负载平衡。3个节点中的每一个都位于具有IP地址的不同计算机上:ip1、ip2、ip3

我的
GoLang
网站,这是
martini
web服务器,有两个URL
/insert
/get

package main

import (
    "github.com/go-martini/martini"
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
    "net/http"
)

const (
    dialStr        = "ip1:port1,ip2:port2,ip3:port3"
    dbName         = "test"
    collectionName = "test"
    elementsCount  = 1000
)

var mainSessionForSave *mgo.Session

func ConnectToMongo() {
    var err error
    mainSessionForSave, err = mgo.Dial(dialStr)
    mainSessionForSave.SetMode(mgo.Monotonic, true)
    if err != nil {
        panic(err)
    }
}

func GetMgoSessionPerRequest() *mgo.Session {
    var sessionPerRequest *mgo.Session
    sessionPerRequest = mainSessionForSave.Copy()
    return sessionPerRequest
}

func main() {
    ConnectToMongo()
    prepareMartini().Run()
}

type Element struct {
    I int `bson:"I"`
}

func prepareMartini() *martini.ClassicMartini {
    m := martini.Classic()
    sessionPerRequest := GetMgoSessionPerRequest()
    m.Get("/insert", func(w http.ResponseWriter, r *http.Request) {
        for i := 0; i < elementsCount; i++ {
            e := Element{I: i}
            err := collection(sessionPerRequest).Insert(&e)
            if err != nil {
                panic(err)
            }
        }
        w.Write([]byte("data inserted successfully"))
    })
    m.Get("/get", func(w http.ResponseWriter, r *http.Request) {
        var element Element
        const findI = 500
        err := collection(sessionPerRequest).Find(bson.M{"I": findI}).One(&element)
        if err != nil {
            panic(err)
        }
        w.Write([]byte("get data successfully"))

    })

    return m
}

func collection(s *mgo.Session) *mgo.Collection {
    return s.DB(dbName).C(collectionName)
}
运行它
go运行攻击者。go
我刚刚请求了URL
http://localhost:3000/get
每秒4000次。当攻击者工作时,我打开了所有3台服务器并运行
htop
命令来监视资源消耗。主节点显示它处于高负载下,CPU约占80%。副手们很平静

为什么? 当我使用mgo时,单调的

mainSessionForSave.SetMode(mgo.Monotonic, true)

。。。我希望从所有节点读取:
ip1、ip2、ip3
,我希望在相同的负载和CPU消耗下观察所有节点。但事实并非如此。我配置错了什么?事实上,
mgo.Monotonic
在我的情况下不起作用,我只从节点读取数据。

使用后您会忘记关闭连接:

defer mainSessionForSave.Close()
也许,这可能是一个原因


注意:确保所有节点都可用:)

只创建一次
sessionPerRequest
prepareMartini
在服务器启动时调用,然后设置
sessionPerRequest
。传递给
m.Get()
的闭包访问该变量。然后,在第一次写入(在测试设置期间)之后:

如果可能的话,单调一致性将开始从从机读取,以便更好地分配负载,并且一旦发生第一次写入,连接将切换到主机

(如果
mgo
在写入主数据后继续从辅助数据读取,则读取不一定反映您刚刚写入的数据,这可能是一件痛苦的事情。而切换到主数据只会得到比从辅助数据获取的数据更新的数据,而不会变老,从而保持单调性。理想情况下,应该是这样无论如何,还是要工作;有关更多信息,请参阅下面的“开放问题”链接。)

解决方案是将创建会话向下推到处理程序中,例如,删除
sessionPerRequest
,并在每个处理程序上放置一些明确的内容,如

coll := mainSessionForSave.Copy().DB(dbName).Collection(collName)

读取所有一致性承诺时应考虑以下因素:目前,在网络分区期间,读取可以看到旧数据和写入,这些数据和写入稍后将回滚,即使
mgo
尝试从主服务器读取时也是如此。(比较和设置没有这个问题,但当然这是一个更大、更慢的操作。)同样值得一读的是,这篇文章仅仅是为了讨论一致性级别和描述不同的数据库行为可能对应用程序的最终用户产生的影响。

我对每个请求都使用这个连接:
sessionPerRequest=mainSessionForSave.Copy()
,所以我不需要在所有站点终止之前关闭它。所有节点都可用。
coll := mainSessionForSave.Copy().DB(dbName).Collection(collName)