使用redgo缓冲池踩得一个小坑

今天测试反馈说有任务卡死在我的服务上,很久都跑不完。经排查发现任务在完成后移除队列时redis直接报了EOF的错误,导致任务一直在不停的重复。且出现频次很高。

出现该现象的主要是那种执行时间超过一分钟的任务,我的idle_time_out设置时间也是一分钟,经百度,发现有人说redigo在使用过程中会出现EOF现象,原因是自带的缓冲池如果空闲超过一定的时间,会被redis sever关闭,再次get时第一次一般就会出现EOF错误。解决办法是添加比超时时间短的间隔心跳。

通过查阅源码,发现redgo的pool池可以通过TestOnBorrow参数设置心跳,具体方式如下如下:

func NewPool(redisURL string) {
	Pool = &redis.Pool{
		MaxIdle:     maxIdle,
		IdleTimeout: idleTimeout,
		MaxActive:   maxActive,
		Wait:        wait,
		Dial: func() (redis.Conn, error) {
			return redis.DialURL(redisURL, redis.DialConnectTimeout(dialTimeout))
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if time.Since(t) < 1*time.Minute {
				return nil
			}
			_, err := c.Do("PING")
			return err
		},
	}
}

超时时间idleTimeout是80秒,每60秒设置一次心跳,这样应该就不会出现超时了。然而现实是并没有解决这个问题,出现确实少了一些,但是也就是一些少了一些,并没有解决这个问题。

然后发现有人说空闲连接被sever关了第一次连接会EOF,但是如果在下次超时之前进行了第二次连接就不会再EOF了,如果真是这样那么直接失败后重传一次是不是就可以成功了呢?可惜依然没有按照预想的方向进行。

痛定思痛,决心回去仔细扒源码。然后经过一番排查,终于发现了问题所在。

1、EOF并不是连接超时引起的,更大的可能是你操作了一个已经关闭的连接

2、官方给出的示例没有问题,但是在概念理解上却存在偏差,个人认为是对redis不熟造成的。

先说百度上常给的解释,基本上是没有问题的,只是他解析的不是EOF出现的根本原因,而是会造成这种结果的一种可能性。需要补充的是get时会EOF的根本原因是连接的一方去使用了一个已经失效的连接。这里补充一下百度解析的过程为什么会造成这样的结果:get方法本身只会删除idle_time_out超时的连接,同时返回一个可用空闲连接或者新连接。什么意思呢,假设客户端的idle_time_out设置的比服务器设置的大,连接因为空闲太久,超过了sever默认的超时时间被服务器关闭了,但是客户端不知道,他还认为这个连接是有效的,于是你就能通过get去拿到这个已经失效的连接,在此连接上做任何操作都会报EOF。这也是最常见的EOF现象,所以官方示例给出的解决办法也是针对这种情况的。需要说明的是TestOnBorrow这个函数并不是用来设置心跳的,或者说他的本意不是设置心跳,代码中的例子也不是一分钟发一个心跳包的意思。他真是的意图是在每次get空闲连接之前先检测这个连接是否真的是正常的,如果正常就返回连接,否则就该关闭连接并寻找先一个空闲连接或者创建新连接。有了这样的功能,我们在get的时候就一定能保证得到的是真正可用的连接。所以如果你的EOF是这中情况,上述方法绝对能够帮到你。当然,这样的操作显然也一定会导致get的时间延长,最坏的情况可能会超出服务器所能接受的范围,可能需要根据实际情况做一些限制。

然而现实永远都有一个如果,是的,我遇到的EOF不是这种情况,根本原因没变,操作了已关闭的连接,但是是超过了我自己设置的idle_time_out时间自动关闭的,而不是被服务器关闭的,但是我的程序却不知道,以为get到的连接依然存在。造成这种情况的原因主要是操作不当:我在get了连接c以后做了一个耗时很长且不可控的与redis无关的操作,然后接着把结果写回到redis,再关闭redis。于是悲剧产生了,在我写回数据之前,c已经被pool关闭了,我没有校验直接再去写,自然就会报EOF。然后在这种前提下我再去重试redis操作,自然也不能解决问题。这也就是为什么之前两次尝试都失败了。

到这里解决办法基本上已经很清晰了,操作和get连接的时间间隔应该在你的控制范围内,直白点就是要小于你和服务器设置的idle_time_out。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值