dongyaofu0599 2019-09-18 17:56
浏览 277
已采纳

如何使用连接池将mgo会话转换为mongo-go-driver客户端?

Long, long ago, when we were using mgo.v2, we created some wrapper functions that copied the session, set the read pref and returned that for consumption by other libraries, e.g.

func NewMonotonicConnection() (conn *Connection, success bool) {
    conn := &Connection{
        session: baseSession.Copy(),
    }
    conn.session.SetMode(mongo.Monotonic, true)

    return conn, true
}

We now just pass the default client (initialized using mongo.Connect and passed into a connection singleton) in an init function and then consumed like this:

func NewMonotonicConnection() (conn *Connection, success bool) {
    conn = defaultConnection
    return conn, true
}

My understanding is that to leverage connection pooling, you need to use the same client (which is contained in defaultConn), and session is now implicitly handled inside of the .All()/cursor teardown. Please correct me if I'm wrong here.

It would be nice if we could still set the readpref on these connections (e.g. set NearestMode on this connection before returning), but what's the community/standard way of doing that?

  • I know I could call mongo.Connect over and over again, but is that expensive?
  • I could create different clients - each client with a different readpref - but I was thinking that if a write occurred on that connection, it wouldn't ever go back to reading from a slave.
  • It looks like I *can create sessions explicitly, but I'm not certain I should or if there are any implications around managing those explicitly in the new driver.
  • 写回答

1条回答 默认 最新

  • dsxfa26482 2019-09-20 20:19
    关注

    There are a couple things I learned on this quest through the mongo-go-driver codebase that I thought I should share with the world before closing this question. If I'm wrong here - please correct me.

    You should not call Connect() over and over if you want to leverage connection pooling. It looked like each time Connect() was called, a new socket was created. This means that there's a risk of socket exhaustion over time unless you are manually defer Close()-ing it each time.

    In mongo-go-driver, sessions are automatically handled under the covers now when you make the call to execute the query (e.g. All()). You can* explicitly create and teardown a session, but you can't consume it using the singleton approach I proposed above without having to change all the caller functions. This is because you can no longer call query operations on the session, you instead have to consume it using a WithSession function at the DB operation itself

    I realized that writeconcern, readpref and readconcern can all be set at the:

    • client level (these are the defaults that everything will use if not overridden)
    • session level
    • database level
    • query level

    So what I did is create Database options and overloaded *mongo.Database e.g.:

    // Database is a meta-helper that allows us to wrap and overload
    // the standard *mongo.Database type
    type Database struct {
        *mongo.Database
    }
    
    // NewEventualConnection returns a new instantiated Connection
    // to the DB using the 'Nearest' read preference.
    // Per https://github.com/go-mgo/mgo/blob/v2/session.go#L61
    // Eventual is the same as Nearest, but may change servers between reads.
    // Nearest: The driver reads from a member whose network latency falls within
    // the acceptable latency window. Reads in the nearest mode do not consider
    // whether a member is a primary or secondary when routing read operations;
    // primaries and secondaries are treated equivalently.
    func NewEventualConnection() (conn *Connection, success bool) {
        conn = &Connection{
            client: baseConnection.client,
            dbOptions: options.Database().
                SetReadConcern(readconcern.Local()).
                SetReadPreference(readpref.Nearest()).
                SetWriteConcern(writeconcern.New(
                    writeconcern.W(1))),
        }
    
        return conn, true
    }
    // GetDB returns an overloaded Database object
    func (conn Connection) GetDB(dbname string) *Database {
        dbByName := &Database{conn.client.Database(dbname, conn.dbOptions)}
    }
    

    This allows me to leverage connection pooling and maintain backwards compatibility with our codebase. Hopefully this helps someone else.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?