redigo 连接池 - 为啥在删除陈旧连接时释放锁

Posted

技术标签:

【中文标题】redigo 连接池 - 为啥在删除陈旧连接时释放锁【英文标题】:redigo connection pool - Why release lock when removing stale connectionredigo 连接池 - 为什么在删除陈旧连接时释放锁 【发布时间】:2017-08-02 22:46:45 【问题描述】:

Redigo 是一个用于 redis 数据库的 golang 客户端。它使用 struct Pool 来维护连接池。这个结构持有一个互斥锁,用于应用程序并行放置和获取连接。

type Pool struct 
    // ...
    IdleTimeout time.Duration
    mu     sync.Mutex
    // Stack of idleConn with most recently used at the front.
    idle list.List

在其get 方法中,连接池首先删除过时(空闲超时)连接。当发现一个过时的连接时,池将其弹出,释放锁,然后关闭连接,再次尝试获取锁。

func (p *Pool) get() (Conn, error) 
    p.mu.Lock()

    // Prune stale connections.

    if timeout := p.IdleTimeout; timeout > 0 
        for i, n := 0, p.idle.Len(); i < n; i++ 
            e := p.idle.Back()
            if e == nil 
                break
            
            ic := e.Value.(idleConn)
                if ic.t.Add(timeout).After(nowFunc()) 
                    break
                
            p.idle.Remove(e)
            p.release()
            // Why does pool unlock and try to acquire lock again?
            p.mu.Unlock()
            // Close this stale connection.
            ic.c.Close()
            p.mu.Lock()
        
    

为什么池解锁并再次尝试获取锁,而不是在函数返回之前解锁?我想关闭一个连接可能会花费很多时间,这会减慢等待这个互斥体的其他 goroutine。

这是整个Pool get method

【问题讨论】:

我认为你的猜测是正确的。关闭连接可能需要未知的时间。暂时锁定池的所有使用似乎是不明智的。 【参考方案1】:

关闭一个连接可能会花费很多时间,这会减慢等待这个互斥体的其他 goroutine。正如@Cerise Limón 所说 - 这次锁定所有池的使用似乎是不明智的。

解锁互斥锁后,其中一个等待的 goroutine 获得互斥锁。虽然 get 方法的 goroutine 仍然需要删除陈旧的连接,但 put 方法的 goroutine 可以将连接放入池中并尽快继续做其他工作。

【讨论】:

"关闭连接可能会花费很多时间" --- 这需要一些详细说明。在引擎盖下它被实现为err := c.fd.Close()。什么情况下会慢? @zerkms 老实说,我无法理解 net/fd_unix.go 中底层的netFD.Close。理论上,当 tcp 正常终止时,每一方(客户端和服务器)都会发送 FIN 并从对方接收 ACK。在此期间,可能存在网络延迟和数据包丢失。

以上是关于redigo 连接池 - 为啥在删除陈旧连接时释放锁的主要内容,如果未能解决你的问题,请参考以下文章

redigo 连接池代码分析

beego redigo redis连接池封装了一下常用的方法 持续学习 持续更新

为啥要用Spring管理连接池,它有啥好处?

数据库连接资源释放

Tomcat JDBC连接池(释放连接)

为啥在部署时验证 Java JDBC 连接池