BTCD源码阅读笔记-connmgr
btcd 是一个golang实现的比特币全节点。
本文是阅读其connmgr包代码的笔记(有些潦草),分析其主要结构。
基于git代码版本: 16327141da8ce4b46b5bac57ba01352943465d9e
Table of Contents
1 connmgr模块
connmgr用来处理节点的连接,定义了一个 ConnManager
,用来管理request connect的连接、断开、失败、重试、限制最大连接数、重试次数等情况。
1.1 主要结构
// ConnManager provides a manager to handle network connections. type ConnManager struct { // The following variables must only be used atomically. connReqCount uint64 // 管理的连接数 start int32 // 启动标识 stop int32 // 停止标志 cfg Config // 配置 wg sync.WaitGroup // 并发连接管理 failedAttempts uint64 // 记录失败次数,如果成功连接则归0 requests chan interface{} // 用于消息传递 quit chan struct{} // 用于退出 } // ConnReq is the connection request to a network address. If permanent, the // connection will be retried on disconnection. type ConnReq struct { // The following variables must only be used atomically. id uint64 // 连接id,采用connReqCoount +1 的方式生成 Addr net.Addr // 连接地址 Permanent bool // 是否是持久连接 conn net.Conn state ConnState // 记录该连接的状态 stateMtx sync.RWMutex retryCount uint32 // 记录持久连接的失败重试次数,如果成功连接则归0 } // Config holds the configuration options related to the connection manager. type Config struct { // Listeners defines a slice of listeners for which the connection // manager will take ownership of and accept connections. When a // connection is accepted, the OnAccept handler will be invoked with the // connection. Since the connection manager takes ownership of these // listeners, they will be closed when the connection manager is // stopped. // // This field will not have any effect if the OnAccept field is not // also specified. It may be nil if the caller does not wish to listen // for incoming connections. Listeners []net.Listener // OnAccept is a callback that is fired when an inbound connection is // accepted. It is the caller's responsibility to close the connection. // Failure to close the connection will result in the connection manager // believing the connection is still active and thus have undesirable // side effects such as still counting toward maximum connection limits. // // This field will not have any effect if the Listeners field is not // also specified since there couldn't possibly be any accepted // connections in that case. OnAccept func(net.Conn) // TargetOutbound is the number of outbound network connections to // maintain. Defaults to 8. TargetOutbound uint32 // RetryDuration is the duration to wait before retrying connection // requests. Defaults to 5s. RetryDuration time.Duration // OnConnection is a callback that is fired when a new outbound // connection is established. OnConnection func(*ConnReq, net.Conn) // OnDisconnection is a callback that is fired when an outbound // connection is disconnected. OnDisconnection func(*ConnReq) // GetNewAddress is a way to get an address to make a network connection // to. If nil, no new connections will be made automatically. GetNewAddress func() (net.Addr, error) // Dial connects to the address on the named network. It cannot be nil. Dial func(net.Addr) (net.Conn, error) }
1.2 主要方法
1.2.1 start
1. go connHandler() 启动处理连接的goroutine 2. go listenHandler() 启动config里注册的listeners 3. go NewConnReq()
1.2.2 connHandler
pending map: 记录pending状态的连接信息 conns map : 记录连接状态的连接信息 for/select: 1. req := <-cm.requests: 1. registerPending 表示 有待发生待连接请求 如何处理: 1.记录该请求到一个pending map中 2. handleConnected 表示 连接建立 如何处理: 1.确保在pending map中添加过该连接 2.pengding map删除条目, conns map中添加连接条目 3.调用manager config配置的OnConnection方法 3. handleDisconnected 表示 连接断开 如何处理: 1.确保在pending或者conns map中添加过该连接,并删除相应条目 2.调用manager config配置的OnDisconnection方法 3.根据 该断开的消息 是否需要重试分开处理 1. 不需要的话,更新状态就结束 2. 需要的话,在一定条件下将连接加到pending map里,然后调用handleFailedConn 4. handleFailed 表示 失败情况 1.确保在pending map中添加过该连接 2.更新连接状态,然后调用handleFailedConn 2. <-cm.quit 退出
1.2.3 handleFailedConn
该方法用来处理连接断开或者其他连接失败的情况 如果连接是持久连接,就在等待一段时间后尝试重连 如果不是,就建立一个新的连接,当重试的次数超过最大值后,那就等待一段时间再重新建立新连接。
1.2.4 listenHandler
该方法等待连接,然后调用config里的OnAccept来处理连接
1.2.5 NewConnReq
该方法创建一个新连接,连接到相应的地址。 具体逻辑: 连接数connReqCount+1 发送registerPending消息(由connHandler接收处理) connHandler处理完register后,调用config的GetNewAddress方法获取新的地址addr 如果获取失败,发送handleFailed方法(由connHandler接收处理) 获取addr成功后,调用connect方法来获取连接
1.2.6 Connect
如果(根据连接id判断)该地址是个新连接,那么设置其id,然后注册到pending map连接里。 调用config里的Dial方法,连接到addr, 如果成功则发送handleConnected方法, 失败则发送handleFailed方法到cm.requests, 由connHandler处理
1.2.7 Disconnect
发送handleDisconnected消息给cm.requests
1.2.8 Stop
该方法先将config里的listeners调用close() 在调用close(cm.quit)发送退出消息告知各个goroutine
1.3 消息传递简图
只有一个cm.requests channel,通过传递不同的msg来让connhandler处理,整体还是比较简明清晰,故不作图了。