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

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

  •  
  •   wewin · 211 天前 · 4760 次点击
    这是一个创建于 211 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我的程序如下,目的就是为了测试,高并发下读取 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   211 天前   ♥ 1
    这种情况瓶颈是在 redis 那边,你要去分析 redis 机器的瓶颈,另外 redis 本身是单线程,异步客户端的话,不需要那么多连接
    wewin
        2
    wewin   211 天前
    @raynor2011 要是有种场景下,就是有很大的并发请求来操作 redis,并且 redis 服务器已经做了相应的优化,这种情况下如何避免不出现这种问题?
    ebingtel
        3
    ebingtel   211 天前
    1. 需要 netstat 看一下 tcp 连接的状态、再看一下 redislog, 才好说
    2. 另外,根据他人的经验 https://www.jianshu.com/p/85cff688d02b,这个 redis 库的连接 不会自动释放,可能是这个问题导致的
    raynor2011
        4
    raynor2011   211 天前
    @wewin redis 本身是单线程,你那么多请求发过去也是顺序执行的
    emeab
        5
    emeab   211 天前
    redis 连接池?
    abccccabc
        6
    abccccabc   211 天前
    tweproxy 代理多个 redis,试下??

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

    把 maxActive 限制一下就可以。
    judeng
        11
    judeng   211 天前
    config get maxclients 看看限制连接数多少
    wewin
        12
    wewin   211 天前
    @Leigg 问题就是没有限制活跃连接数的问题。本来想法是 1000 个 goroutine 最多也就一个 1000 个连接,redis 的 maxclients 是 10000。所以没有想到是这里的问题。
    wewin
        13
    wewin   211 天前
    @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   211 天前 via iPhone
    如果是 Redis 是 10000 不应该出现这个问题 还得接着查
    petelin
        15
    petelin   211 天前 via iPhone
    @rrfeng 他不才连了 1000 个吗 为什么说瞬间就超了?
    feelinglucky
        16
    feelinglucky   211 天前
    Redis 并发的问题:1、检查 Redis 的配置,本身 Redis 是单线程的,所以再多的请求都是顺序发送的 2、本地客户端发那么多并发的话,流量也是量级的了,就应该考虑其他方式了。
    Mirana
        17
    Mirana   211 天前
    tcp 连接被对端关了,tcpdump 抓包看看
    Hellert
        18
    Hellert   211 天前
    你是不是开了 ss ?另外,是不是设置了 http_proxy 环境变量?
    你把 http_proxy 和 https_proxy 环境变量删除掉,然后关掉 ss 试试。

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