CockroachDB重启后应用报conn closed无法自动重连问题咨询
问题根因
你当前的实现仅判断了全局*pgx.Conn变量是否为nil,但CockroachDB重启后原有连接被服务端主动关闭,此时全局db变量仍然是非空状态,仅底层TCP连接已失效,GetDB()不会触发重连逻辑,直接返回已关闭的连接就会抛出conn closed错误。
解决方案
首选方案:使用pgx官方连接池*pgxpool.Pool(生产环境推荐)
pgx自带的连接池会自动维护连接有效性,获取连接时会自动检测连接是否存活,失效连接会被自动销毁并重建新连接,无需手动实现重连逻辑,同时还能支撑并发数据库请求,是生产环境的标准用法。
示例代码如下:
import ( "context" "fmt" "os" "time" "github.com/jackc/pgx/v5/pgxpool" ) var dbPool *pgxpool.Pool func InitCockroachDBPool() { dbUri := "testurl" // 解析连接配置 config, err := pgxpool.ParseConfig(dbUri) if err != nil { fmt.Fprintf(os.Stderr, "Unable to parse database config: %v\n", err) os.Exit(1) } // 自定义连接池参数,可根据业务场景调整 config.MaxConns = 10 // 最大连接数 config.MinConns = 2 // 最小空闲连接数 config.MaxConnLifetime = 30 * time.Minute // 连接最大生命周期 // 初始化连接池 pool, err := pgxpool.NewWithConfig(context.Background(), config) if err != nil { fmt.Fprintf(os.Stderr, "Unable to create connection pool: %v\n", err) os.Exit(1) } // 校验初始连接可用性 if err := pool.Ping(context.Background()); err != nil { fmt.Fprintf(os.Stderr, "Unable to ping database: %v\n", err) os.Exit(1) } dbPool = pool } // 返回连接池实例,所有数据库操作直接用这个实例即可 func GetDBPool() *pgxpool.Pool { if dbPool == nil { InitCockroachDBPool() } return dbPool }
备选方案:单连接增加有效性校验(仅适合测试/低并发场景)
如果你坚持使用单连接,需要在获取连接时增加存活校验,同时加互斥锁避免并发重连的竞态问题,示例代码如下:
import ( "context" "fmt" "os" "sync" "github.com/jackc/pgx/v5" ) var db *pgx.Conn var dbMu sync.Mutex // 防止多goroutine同时触发重连 func ConnectCockroachDB() { var dbUri string = "testurl" conn, err := pgx.Connect(context.Background(), dbUri) if err != nil { fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err) os.Exit(1) } db = conn } func GetDB() *pgx.Conn { dbMu.Lock() defer dbMu.Unlock() // 增加连接有效性校验,ping失败则触发重连 if db == nil || db.Ping(context.Background()) != nil { ConnectCockroachDB() } return db }
注意:pgx的单连接不支持并发执行SQL操作,高并发场景下会出现串行阻塞甚至数据错乱问题,生产环境不要用该方案。
额外优化建议
- 执行SQL操作时如果捕获到连接关闭类错误,可以增加1-2次重试逻辑,进一步提升服务可用性
- 连接池参数需要根据业务的QPS、数据库最大连接限制做适配,避免连接数占满数据库资源
内容的提问来源于stack exchange,提问作者wahyu eko hadi saputro
相关产品推荐
相关产品推荐

