美文网首页
redigo连接池不入坑

redigo连接池不入坑

作者: colors_liu | 来源:发表于2018-08-31 15:42 被阅读0次

    写在前面

    用go开发web项目时,经常会用到redis,推荐使用redigo包,目前有4800多个star,基本不会有太大的坑。
    https://github.com/gomodule/redigo

    聊聊redis的i/o并发

    Redis is single-threaded with epoll/kqueue and scales indefinitely in terms of I/O concurrency.

    redis是单线程的,但又是处实现并发的呢?参照地址:
    https://stackoverflow.com/questions/10489298/redis-is-single-threaded-then-how-does-it-do-concurrent-i-o
    redis是使用事件循环来实现并发的,事件是原子性的,还没有额外的锁开销。设计非常精妙。

    我们在读写redis时,大多都是网络传输层的开销,redis计算是非常快的。所以我们尽量用多个连接去读写redis,相当于并发做网络传递,排队等着redis计算,不能让redis计算引擎闲下来。

    使用连接池

    在使用redigo的时候,强烈建议使用连接池,不然每次都得tcp建链,不嫌麻烦吗。而使用连接池的话,只管去get其它都给交类库去处理。示例代码如下:

    var redisClient *redis.Pool
    func init() {
        maxIdle := MaxIdle
        if v, ok := conf["MaxIdle"]; ok {
            maxIdle = int(v.(int64))
        }   
        maxActive := MaxActive
        if v, ok := conf["MaxActive"]; ok {
            maxActive = int(v.(int64))
        }   
    
        // 建立连接池
        redisClient = &redis.Pool{
            MaxIdle:     maxIdle,
            MaxActive:   maxActive,
            IdleTimeout: MaxIdleTimeout * time.Second,
            Wait:        true,
            Dial: func() (redis.Conn, error) {
                con, err := redis.Dial("tcp", conf["Host"].(string),
                    redis.DialPassword(conf["Password"].(string)),
                    redis.DialDatabase(int(conf["Db"].(int64))),
                    redis.DialConnectTimeout(timeout*time.Second),
                    redis.DialReadTimeout(timeout*time.Second),
                    redis.DialWriteTimeout(timeout*time.Second))
                if err != nil {
                    return nil, err 
                }   
                return con, nil 
            },  
        }
    }
    // 从池里获取连接
    rc := RedisClient.Get()
    // 用完后将连接放回连接池
    defer rc.Close()
    // 错误判断
    if conn.Err() != nil {
      //TODO
    }
    

    其它有几个需要注意的地方:

    • MaxActive 最大连接数,即最多的tcp连接数,一般建议往大的配置,但不要超过操作系统文件句柄个数(centos下可以ulimit -n查看)。
    • MaxIdle 最大空闲连接数,即会有这么多个连接提前等待着,但过了超时时间也会关闭。
    • IdleTimeout 空闲连接超时时间,但应该设置比redis服务器超时时间短。否则服务端超时了,客户端保持着连接也没用。
    • Wait 这是个很有用的配置。好多东抄抄本抄抄的文章都没有提。如果超过最大连接,是报错,还是等待。

    常见报错

    连接池消耗殆尽

    redigo: connection pool exhausted
    

    redigo常常会有这样的报错。我们来从redigo源码上来分析这个问题。

        // Handle limit for p.Wait == false.
        if !p.Wait && p.MaxActive > 0 && p.active >= p.MaxActive {
            p.mu.Unlock()
            return nil, ErrPoolExhausted
        }  
    

    当Wait==false,并且当前有效连接>=最大连接数里就报这个错了。要解决这个问题的话,可以修改这个参数:

    • MaxActive 可以把MaxActive调大(一般设置为500,1000问题都不大。)但如果redis服务器负载已经很高了(可以看redis-server CPU占用),去调大MaxActive就没多大意义。还是需要根据实际情况来权衡。
    • Wait 可以把Wait设置为true。wait的话必然会加大响应,如果对响应时间要求较高的话,还得从别的途径来解决。
    • 还可以加从库通过读写分离来解决。特别是PHP,没有常驻的redis连接池,建议增加多个从库。

    相关文章

      网友评论

          本文标题:redigo连接池不入坑

          本文链接:https://www.haomeiwen.com/subject/ebnswftx.html