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就可以了。
golang:mgo剖析之Session的更多相关文章
- golang mgo的mongo连接池设置:必须手动加上maxPoolSize
本司礼物系统使用了golang的 mongo库 mgo,中间踩了一些坑,总结下避免大家再踩坑 golang的mgo库说明里是说明了开启连接复用的,但观察实验发现,这并没有根本实现连接的控制,连接复用仅 ...
- golang 性能剖析pprof
作为一个golang coder,使用golang编写代码是基本的要求. 能够写出代码,并能够熟悉程序执行过程中各方面的性能指标,则是更上一层楼. 如果在程序出现性能问题的时候,可以快速定位和解决问题 ...
- golang context 剖析 1.7.4 版本
1. 内部结构之 - timerCtx . type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. dead ...
- Golang mgo 模糊查询的使用
在日常使用的Mongodb中,有一项功能叫做模糊查询(使用正则匹配),例如: db.article.find({"title": {$regex: /a/, $options: & ...
- golang学习之mgo操作mongodb
mgo是mongodb的golang驱动,测试代码: // mgotest project main.go package main import ( "fmt" "ti ...
- mgo中DBRef-数据添加测试
2014-1-25 在设计mongo数据库时遇到这样一个问题,日志信息表需要引用人员信息表的数据.如果是结构化数据库,基本上不用想太多的东西.由于刚接触非结构化数据库,按着书上的理解由于日志数量较多, ...
- session机制详解以及session的相关应用
session是web开发里一个重要的概念,在大多数web应用里session都是被当做现成的东西,拿来就直接用,但是一些复杂的web应用里能拿来用的session已经满足不了实际的需求,当碰到这样的 ...
- mgo中DBRef-数据查询测试
下午对数据查询进行了代码测试: package main import ( "crypto/rand" "encoding/hex" "fmt&quo ...
- golang (4) golang 操作mongdb
1. 数据按照时间聚合操作 1.1 正常的数据结构 { "_id" : ObjectId("5cac8d7b1202708adf5d4b64"), " ...
随机推荐
- 启动子Activity
启动普通子Activity: 一个activity启动另一个activity最简单的方式是使用 startActivity(Intent) 方法: public void startActivity( ...
- 二分法 Binary Search
二分法还是比较常见和简单的,之前也遇到过一些二分的相关题目,虽然不难,但是每次都需要在边界问题上诸多考虑,今天听了九章算法的课程,学习到一种方法使得边界问题简单化. 二分法的几个注意点: 1. mid ...
- sqlserver操作各种文件方法
******* 导出到excelEXEC master..xp_cmdshell ''bcp SettleDB.dbo.shanghu out c:\temp1.xls -c -q -S"G ...
- 学习成绩>=90分的同学用A表示,60-89分之间的用B表示,60分以下的用利用条件运算符的嵌套来完成此题:C表示。
# -*- coding: utf8 -*- # Author:wxq #python 2.7 #题目:学习成绩>=90分的同学用A表示,60-89分之间的用B表示,60分以下的用利用条件运算符 ...
- mysql环境变量配置(复制)
前面步骤完成后安装好MySQL,为MySQL配置环境变量.MySQL默认安装在C:\Program Files下. 1)新建MYSQL_HOME变量,并配置:C:\Program Files\MySQ ...
- 在控制器“xxxx”上找不到与该请求匹配的操作
Message:"找不到与请求 URI"http://localhost:8091/Api/CommonApi/SelectBind/GetBudCategoryListByCID ...
- RUBY 模拟rtsp消息
require 'rtsp/client' require 'log_switch' require 'socket' RTSP::Client.log? # => false RTSP::Cl ...
- java中的读/写锁
读写锁接口:ReadWriteLock,它的具体实现类为:ReentrantReadWriteLock 使用场景: 对于一个资源,读读能共存,读写不能共存,写写不能共存. 锁降级:从写锁变成读锁: 锁 ...
- OtherStream
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.DataInputStream; import ...
- 删除ARCSDE表空间和用户后,新建时出现error -1:O的解决办法
对于刚开始使用arcsde的用户,可能会出现各种问题,慢慢来就会找到解决办法 当我们删除用户和表空间时,在服务器本地还保留这sde.dbf文件(删除时选择了删除本地文件,不知道为什么), 我们可以换一 ...