发现有个 set_keepalive 的方法,查了一下官方文档,方法的原型是 syntax: ok, err = red:set_keepalive(max_idle_timeout, pool_size) 貌似 max_idle_timeout 这个参数,就是我们所缺少的东西,然后进一步跟踪源码,看看里面是怎么保证连接有效的。
function _M.set_keepalive(self, ...)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
if self.subscribed then
return nil, "subscribed state"
end
return sock:setkeepalive(...)
end
至此,已经清楚了,使用了 tcp 的 keepalive 心跳机制。
于是,通过与 Radix.v2 的作者一些讨论,选择自己在 redis 这层使用心跳机制,来解决这个问题。
四、最后的解决方案
在创建连接池之后,起一个 goroutine,每隔一段 idleTime 发送一个 PING 到 Redis server。其中,idleTime 略小于 Redis server 的 timeout 配置。
连接池初始化部分代码如下:
p, err := pool.New("tcp", u.Host, concurrency)
errHndlr(err)
go func() {
for {
p.Cmd("PING")
time.Sleep(idelTime * time.Second)
}
}()
使用 redis 传输数据部分代码如下:
func redisDo(p *pool.Pool, cmd string, args ...interface{}) (reply *redis.Resp, err error) {
reply = p.Cmd(cmd, args...)
if err = reply.Err; err != nil {
if err != io.EOF {
Fatal.Println("redis", cmd, args, "err is", err)
}
}
return
}
其中,Radix.v2 连接池内部进行了连接池内连接的获取和放回,代码如下:
// Cmd automatically gets one client from the pool, executes the given command
// (returning its result), and puts the client back in the pool
func (p *Pool) Cmd(cmd string, args ...interface{}) *redis.Resp {
c, err := p.Get()
if err != nil {
return redis.NewResp(err)
}
defer p.Put(c)
return c.Cmd(cmd, args...)
}
这样,我们就有了 keepalive 的机制,不会出现 timeout 的连接了,从 redis 连接池里面取出的连接都是可用的连接了。看似简单的代码,却完美的解决了连接池里面超时连接的问题。同时,就算 Redis server 重启等情况,也能保证连接自动重连。
【编辑推荐】
使用 Go 进行 iOS 和 Android 编程
再论 golang 环境配置建议
来自 Google 的高可用架构理念与实践
Redis整合Spring结合使用缓存实例
动画开发者福音? 白鹭时代DragonBones全面揭秘
(责任编辑:admin)