V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
wewin
V2EX  ›  Go 编程语言

请教大佬一个高并发下操作 Reids 出现 read: connection reset by peer 的问题

  •  
  •   wewin · 2019-06-24 08:15:06 +08:00 · 10452 次点击
    这是一个创建于 1985 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我的程序如下,目的就是为了测试,高并发下读取 redis 会出现什么问题:

    package main
    
    import (
    	"fmt"
    	"time"
    
    	"github.com/garyburd/redigo/redis"
    )
    
    var pool *redis.Pool
    
    type User struct {
    	ID       int    `json:"id"`
    	Name     string `json:"name"`
    	Password string `json:"password"`
    }
    
    func init() {
    	pool = &redis.Pool{
    		// 初始化链接数量
    		MaxIdle:     16,
    		MaxActive:   0,
    		IdleTimeout: 300 * time.Second,
    		Dial: func() (redis.Conn, error) {
    			return redis.Dial("tcp", "127.0.0.1:6379")
    		},
    	}
    }
    
    func idIncr(conn redis.Conn) (id int, err error) {
    	res, err := conn.Do("incr", "users_id_for_test")
    	if err != nil {
    		fmt.Printf("id incr error: %v\n", err)
    		return
    	}
    	id = int(res.(int64))
    	fmt.Printf("id: %v\n", id)
    	return
    }
    
    func Register() (err error) {
    	conn := pool.Get()
    	defer conn.Close()
    
    	// id 自增 1,作为下个用户 id
    	id, err := idIncr(conn)
    	if err != nil {
    		return
    	}
    
    	_, err = conn.Do("rpush", "usersList", id)
    	if err != nil {
    		fmt.Printf("set user to reids error: %v", err)
    		return
    	}
    	return
    }
    
    // 测试高并发下操作 redis
    func main() {
    	for i := 0; i < 1000; i++ {
    		go Register()
    	}
    	time.Sleep(10 * time.Second)
    }
    

    这种情况下会有大量的报错

    id incr error: read tcp 127.0.0.1:54156->127.0.0.1:6379: read: connection reset by peer
    

    减少 goroutine 的数量不会有问题

    应该是在高并发下 redis 的可用连接数不够了导致的问题,有理解的比较深入的大佬给个仔细的讲解吗?最好能给上解决方案,谢谢!

    25 条回复    2019-06-25 11:08:31 +08:00
    raynor2011
        1
    raynor2011  
       2019-06-24 08:31:10 +08:00   ❤️ 1
    这种情况瓶颈是在 redis 那边,你要去分析 redis 机器的瓶颈,另外 redis 本身是单线程,异步客户端的话,不需要那么多连接
    wewin
        2
    wewin  
    OP
       2019-06-24 08:38:06 +08:00
    @raynor2011 要是有种场景下,就是有很大的并发请求来操作 redis,并且 redis 服务器已经做了相应的优化,这种情况下如何避免不出现这种问题?
    ebingtel
        3
    ebingtel  
       2019-06-24 08:39:01 +08:00
    1. 需要 netstat 看一下 tcp 连接的状态、再看一下 redislog, 才好说
    2. 另外,根据他人的经验 https://www.jianshu.com/p/85cff688d02b,这个 redis 库的连接 不会自动释放,可能是这个问题导致的
    raynor2011
        4
    raynor2011  
       2019-06-24 08:53:37 +08:00
    @wewin redis 本身是单线程,你那么多请求发过去也是顺序执行的
    2kCS5c0b0ITXE5k2
        5
    2kCS5c0b0ITXE5k2  
       2019-06-24 08:55:57 +08:00
    redis 连接池?
    abccccabc
        6
    abccccabc  
       2019-06-24 09:04:09 +08:00
    tweproxy 代理多个 redis,试下??

    当前因为单个 redis 已经顶不住了,那就多个一块顶。
    q13859601
        7
    q13859601  
       2019-06-24 09:40:56 +08:00
    MaxActive: 0,这个是代表不限制么,是不是可以设置一下连接池的等待时间参数
    petelin
        8
    petelin  
       2019-06-24 09:43:32 +08:00 via iPhone
    如果真有并发链接的需求 也可以转化为异步队列读写
    我估摸着应该是 Redis 有个配置最大连接数的东西
    Leigg
        9
    Leigg  
       2019-06-24 10:04:24 +08:00 via iPhone
    你并发有多少??连接池最大数量限制为 redis 最大连接数以内不就行了。问题不在这
    rrfeng
        10
    rrfeng  
       2019-06-24 10:16:19 +08:00
    redis-server 默认有最大连接数限制( 10000 ),你这个 redis.Pool{ maxActive: 0} 不限制活跃连接数,瞬间就超了,超了之后就被 redis-server 断了。

    把 maxActive 限制一下就可以。
    judeng
        11
    judeng  
       2019-06-24 10:26:43 +08:00
    config get maxclients 看看限制连接数多少
    wewin
        12
    wewin  
    OP
       2019-06-24 12:57:00 +08:00
    @Leigg 问题就是没有限制活跃连接数的问题。本来想法是 1000 个 goroutine 最多也就一个 1000 个连接,redis 的 maxclients 是 10000。所以没有想到是这里的问题。
    wewin
        13
    wewin  
    OP
       2019-06-24 13:02:27 +08:00
    @rrfeng
    @judeng

    问题就是没有限制活跃连接数的问题。本来想法是 1000 个 goroutine 最多也就一个 1000 个连接,而 redis 的 maxclients 是 10000,所以没有想到是这里的问题。

    通过 runtime.NumGoroutine() 查到 goroutine 的峰值是 1001。也就是一个主 goroutine + 1000 个 'Register' goroutine。

    经过测试, maxActive 最好限制到 1000 左右,太少会报错 'redigo: connection pool exhausted',不限制就是报错 'read: connection reset by peer'
    petelin
        14
    petelin  
       2019-06-24 13:52:20 +08:00 via iPhone
    如果是 Redis 是 10000 不应该出现这个问题 还得接着查
    petelin
        15
    petelin  
       2019-06-24 13:54:02 +08:00 via iPhone
    @rrfeng 他不才连了 1000 个吗 为什么说瞬间就超了?
    feelinglucky
        16
    feelinglucky  
       2019-06-24 14:10:20 +08:00
    Redis 并发的问题:1、检查 Redis 的配置,本身 Redis 是单线程的,所以再多的请求都是顺序发送的 2、本地客户端发那么多并发的话,流量也是量级的了,就应该考虑其他方式了。
    Mirana
        17
    Mirana  
       2019-06-24 14:52:18 +08:00
    tcp 连接被对端关了,tcpdump 抓包看看
    Hellert
        18
    Hellert  
       2019-06-24 14:57:45 +08:00
    你是不是开了 ss ?另外,是不是设置了 http_proxy 环境变量?
    你把 http_proxy 和 https_proxy 环境变量删除掉,然后关掉 ss 试试。

    我之前遇到过类似的问题,是因为 go 写的网络程序默认是走代理的,这个错误是 ss 那边报的。
    dafsic
        19
    dafsic  
       2019-06-24 15:45:50 +08:00
    改为短链接,防止重用 tcp 连接
    stone1342006
        20
    stone1342006  
       2019-06-24 18:34:12 +08:00
    系统 somaxconn 设置大一点试试
    onepunch
        21
    onepunch  
       2019-06-24 21:10:55 +08:00
    redis 管道技术了解一下,最近面试都在问这个 -,- 目前没碰到一个会的
    freestyle
        22
    freestyle  
       2019-06-24 22:12:03 +08:00 via iPhone
    redigo: connection pool exhaustet 是 redigo 包返回的错误,和初始化 pool 的选项有关,你设置 maxActive 的同时把 wait=true 再试试效果
    wewin
        23
    wewin  
    OP
       2019-06-25 07:34:26 +08:00
    @freestyle redigo: connection pool exhaustet 是和 maxActive 有关的,maxActive 需要设置和操作 redis 的 goroutine 数量差不多,或者多余 goroutine 的数量就不会出现 redigo: connection pool exhaustet 的错误
    freestyle
        24
    freestyle  
       2019-06-25 09:40:58 +08:00 via iPhone
    @wewin 你得的结论不太严谨 不知道你没有有看 redigo 的 pool 的详细注释 wait 是表示当 pool 中没有可用的连接时是报错还是等待 .
    SmartKeyerror
        25
    SmartKeyerror  
       2019-06-25 11:08:31 +08:00
    Redis 内部使用 epoll 事件驱动模型, 只要 redis-server 所在的服务器能够打开的文件描述符以及内存足够, 一般是不会出现连接数不够用的。 在我的机子上跑你这个代码,goroutine 的数量开到 10000 也不会有任何报错, 但是当同时运行的 goroutine 数量超过了 redis-server 默认的 maxClients(10000)数量之后, 会抛出 id incr error: ERR max number of clients reached, 但是这也是意料之中的问题。 所以我建议你看一下是不是 redigo 的版本或者是服务器的问题, 检查下服务器 TCP 最大连接数, 能够打开的最大文件数这些。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3707 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 101ms · UTC 10:24 · PVG 18:24 · LAX 02:24 · JFK 05:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.