• golang:mgo剖析之Session


      golang操作mongo使用的包是"gopkg.in/mgo.v2",coding过程中需要并发读写mongo数据库,简单观摩了下源码,记录下自己的一些理解,如有错误,敬请斧正。 

      一般来说,我们直接这样创建一个session:

    Session, err = mgo.Dial(URL)

         if err != nil {

            log.Println(err)

     }

      来看看Dial这个函数做了什么:

    func Dial(url string) (*Session, error) {

        session, err := DialWithTimeout(url, 10*time.Second)

        if err == nil {

            session.SetSyncTimeout(1 * time.Minute)

            session.SetSocketTimeout(1 * time.Minute)

        }

        return session, err

    }

      调用DialWithTimeout函数设置默认的超时时间是10秒。该函数中调用了DialWithInfo这个函数,而DialWithInfo函数中比较重要是是调用了newSession,看看这个函数做了什么操作:

    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

    }

      返回的session设置了一些默认的参数,暂时先忽略,直接看看Session的数据结构:

    type Session struct {

        m                sync.RWMutex

        cluster_         *mongoCluster

        slaveSocket      *mongoSocket

        masterSocket     *mongoSocket

        slaveOk          bool

        consistency      Mode

        queryConfig      query

        safeOp           *queryOp

        syncTimeout      time.Duration

        sockTimeout      time.Duration

        defaultdb        string

        sourcedb         string

        dialCred         *Credential

        creds            []Credential

        poolLimit        int

        bypassValidation bool

    }

      m是mgo.Session的并发锁,因此所有的Session实例都是线程安全的。slaveSocket,masterSocket代表了该Session到mongodb主节点和从节点的一个物理连接的缓存。而Session的策略总是优先使用缓存的连接。是否缓存连接,由consistency也就是该Session的模式决定。假设在并发程序中,使用同一个Session实例,不使用Copy,而该Session实例的模式又恰好会缓存连接,那么,所有的通过该Session实例的操作,都会通过同一条连接到达mongodb。虽然mongodb本身的网络模型是非阻塞通信,请求可以通过一条链路,非阻塞地处理,但是会影响效率。

      其次mgo.Session缓存的一主一从连接,实例本身不负责维护。也就是说,当slaveSocket,masterSocket任意其一,连接断开,Session自己不会重置缓存,该Session的使用者如果不主动重置缓存,调用者得到的将永远是EOF。这种情况在主从切换时就会发生,在网络抖动时也会发生。

      mgo的DB句柄需要你做一个copy操作:

    // Copy works just like New, but preserves the exact authentication

    // information from the original session.

    func (s *Session) Copy() *Session {

        s.m.Lock()

        scopy := copySession(s, true)

        s.m.Unlock()

        scopy.Refresh()

        return scopy

    }

      copySession将源Session浅拷贝到临时Session中,这样源Session的配置就拷贝到了临时Session中。关键的Refresh,将源Session浅拷贝到临时Session的连接缓存指针,也就是slaveSocket,masterSocket置为空,这样临时Session就不存在缓存连接,而转为去尝试获取一个空闲的连接。

    mgo自身维护了一套到mongodb集群的连接池。这套连接池机制以mongodb数据库服务器为最小单位,每个mongodb都会在mgo内部,对应一个mongoServer结构体的实例,一个实例代表着mgo持有的到该数据库的连接。看看这个连接池的定义:

    type mongoServer struct {

        sync.RWMutex

        Addr          string

        ResolvedAddr  string

        tcpaddr       *net.TCPAddr

        unusedSockets []*mongoSocket

        liveSockets   []*mongoSocket

        closed        bool

        abended       bool

        sync          chan bool

        dial          dialer

        pingValue     time.Duration

        pingIndex     int

        pingCount     uint32

        pingWindow    [6]time.Duration

        info          *mongoServerInfo

    }

      info代表了该实例对应的数据库服务器在集群中的信息——是否master,ReplicaSetName等。而两个Slice,就是传说中的连接池。unusedSockets存储当前空闲的连接,liveSockets存储当前活跃中的连接,Session缓存的连接就同时存放在liveSockets切片中,而临时Session获取到的连接就位于unusedSockets切片中。

      每个mongoServer都会隶属于一个mongoCluster结构,相当于mgo在内部,模拟出了mongo数据库集群的模型。

    type mongoCluster struct {

        sync.RWMutex

        serverSynced sync.Cond

        userSeeds    []string

        dynaSeeds    []string

        servers      mongoServers

        masters      mongoServers

        references   int

        syncing      bool

        direct       bool

        failFast     bool

        syncCount    uint

        setName      string

        cachedIndex  map[string]bool

        sync         chan bool

        dial         dialer

    }

      mongoCluster持有一系列mongoServer的实例,以主从结构分散到两个数组中。  每个Session都会存储自己对应的,要操作的mongoCluster的引用。

      前面的描述可以总结成下面这张图:

     

      那么我们在使用的时候就可以创建一个Session,然后clone操作,用clone得到的copysession完成操作,结束后关闭这个copysession就可以了。

     
     
  • 相关阅读:
    【转】VS2010中 C++创建DLL图解
    [转]error: 'retainCount' is unavailable: not available in automatic reference counting mode
    [转]关于NSAutoreleasePool' is unavailable: not available in automatic reference counting mode的解决方法
    【转】 Tomcat v7.0 Server at localhost was unable to start within 45
    【转】Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If
    【转】SVN管理多个项目版本库
    【转】eclipse安装SVN插件的两种方法
    【转】MYSQL启用日志,和查看日志
    【转】Repository has not been enabled to accept revision propchanges
    【转】SVN库的迁移
  • 原文地址:https://www.cnblogs.com/ExMan/p/11436515.html
Copyright © 2020-2023  润新知