go-redis源码解析:连接池原理

news2024/11/13 12:13:53

1. 执行命令的入口方法

redis也是通过hook执行命令,initHooks时,会将redis的hook放在第一个

img

通过hook调用到process方法,process方法内部再调用_process

img

2. 线程池初始化

redis在新建单客户端、sentinel客户端、cluster客户端等,都会newConnPool初始化线程池

2.1.1. NewClient方式初始化连接池

// NewClient returns a client to the Redis Server specified by Options.
func NewClient(opt *Options) *Client {
	opt.init()

	c := Client{
		baseClient: &baseClient{
			opt: opt,
		},
	}
	c.init()
	// 初始化线程池
	c.connPool = newConnPool(opt, c.dialHook)

	return &c
}

2.1.2. NewFailoverClient方式初始化连接池

// NewFailoverClient returns a Redis client that uses Redis Sentinel
// for automatic failover. It's safe for concurrent use by multiple
// goroutines.
// zhmark 2024/6/13 NewFailoverClient
func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
	if failoverOpt.RouteByLatency {
		panic("to route commands by latency, use NewFailoverClusterClient")
	}
	if failoverOpt.RouteRandomly {
		panic("to route commands randomly, use NewFailoverClusterClient")
	}

	sentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs))
	copy(sentinelAddrs, failoverOpt.SentinelAddrs)

	// todo:2024/6/26 有问题,每次都是换成1、3、2
	// 将 sentinelAddrs 切片中的元素顺序随机打乱,实现随机化效果
	rand.Shuffle(len(sentinelAddrs), func(i, j int) {
		//交换 sentinelAddrs 中第 i 个和第 j 个元素
		sentinelAddrs[i], sentinelAddrs[j] = sentinelAddrs[j], sentinelAddrs[i]
	})

	failover := &sentinelFailover{
		opt:           failoverOpt,
		sentinelAddrs: sentinelAddrs,
	}

	opt := failoverOpt.clientOptions()
	// 初始化赋值连接建立函数
	opt.Dialer = masterReplicaDialer(failover)
	opt.init()

	var connPool *pool.ConnPool

	rdb := &Client{
		baseClient: &baseClient{
			opt: opt,
		},
	}
	rdb.init()
// 初始化线程池
	connPool = newConnPool(opt, rdb.dialHook)
	rdb.connPool = connPool
	rdb.onClose = failover.Close

	failover.mu.Lock()
	// 关闭老的有问题的地址连接
	//如:发现新读取的主节点地址和本地保存的不一样,将之前和老的主节点连接断开
	// addr是新的master地址
	failover.onFailover = func(ctx context.Context, addr string) {
		_ = connPool.Filter(func(cn *pool.Conn) bool {
			// 如果连接的远程地址与 addr 不同,则返回 true,表示要关闭此连接;否则返回 false,表示保留该连接
			return cn.RemoteAddr().String() != addr
		})
	}
	failover.mu.Unlock()

	return rdb
}

2.1. NewClusterClient方式初始化线程池

cluster模式和上面的NewClient、NewFailoverClient不一样。cluster模式new的时候不会初始化连接池,而是等执行命令时,获取所有节点,每个节点新建一个redisClient,每个client单独一个连接池

2.1.1. 初始化NewClusterClient时不会新建连接池

// NewClusterClient returns a Redis Cluster client as described in
// http://redis.io/topics/cluster-spec.
func NewClusterClient(opt *ClusterOptions) *ClusterClient {
	// 初始化opt,其中会初始化NewClient方法
  opt.init()

	c := &ClusterClient{
		opt:   opt,
		nodes: newClusterNodes(opt),
	}

	// 获取所有主从节点信息,并保存在本地
	c.state = newClusterStateHolder(c.loadState)
	// 保存命令详情
	c.cmdsInfoCache = newCmdsInfoCache(c.cmdsInfo)
	c.cmdable = c.Process

	c.initHooks(hooks{
		dial:       nil,
		process:    c._process,
		pipeline:   c.processPipeline,
		txPipeline: c.processTxPipeline,
	})

	return c
}

2.1.2. 执行命令时,通过cmdNode执行到NewClient,初始化线程池

img

通过clOpt的NewClient方法,初始化client,进而初始化线程池

img

2.1.3. 然而clOpt的NewClient方法什么时候初始化赋值的呢

在NewClusterClient方法的opt.init()中

img

img

3. 如何新建连接

总览图

img

3.1.1. 第一次执行命令时,go-redis会先通过cmdNode方法,获取所有的节点信息

img

3.1.2. 底层调用到ClusterSlots方法,触发redis.go中_process方法,内部调用_withConn方法,通过getConn方法获取可用连接

img

3.1.3. getConn方法内部发现无可用连接,则会调用newConn

3.1.4. newConn内部,调用连接池的dialConn方法触发调用

img

3.1.5. dialConn调用配置项的Dialer方法

img

3.1.6. p.cfg.Dialer在newConnPool时候初始化的,通过Dialer方法,触发dialer

img

3.1.7. 而dialer是newClient时传入的dialhook,至此直接触发了dialhook

img

3.1.8. sentinel模式也是在NewFailoverClient时传入的dialhook

img

3.1.9. redis自己的dialHook内部,执行的是opt的Dialer方法

img

3.1.10. 此Dialer方法是在NewClient中opt.init()初始化方法中赋值的,如果没有自定义,就用默认的建连方法

img

3.1.11. 默认的建连方法很简单,调用go底层的net建立连接

img

3.1.12. sentinel模式不一样,NewFailoverClient方法有自定义建连方法

img

3.1.13. 里面实现了读写分离

img

4. 闲置连接如何关闭

看是否有配置MinIdleConnsMaxIdleConns。如果有配置了MinIdleConns,那么在NewConnPool、popIdle、removeConn时,都会调用checkMinIdleConns补充创建最低闲置连接数

// Minimum number of idle connections which is useful when establishing
// new connection is slow.
// Default is 0. the idle connections are not closed by default.
MinIdleConns int
// Maximum number of idle connections.
// Default is 0. the idle connections are not closed by default.
MaxIdleConns int

img

每次执行完方法,会释放连接

img

img

5. 如何控制闲置连接数大小

6. 如何控制总连接数

poolSize:控制最大并发量

turn可能为0,闲置连接数为最大poolSize

img

img

img

7. 如何保持连接池内的连接健康

每次Get连接时,会检查连接是否健康

func (p *ConnPool) Get(ctx context.Context, isReadCmd bool) (*Conn, error) {
	if p.closed() {
		return nil, ErrClosed
	}

	// 排队
	if err := p.waitTurn(ctx); err != nil {
		return nil, err
	}

	for {
		p.connsMu.Lock()
		// 获取一个可用的连接
		cn, err := p.popIdle(isReadCmd)
		p.connsMu.Unlock()

		if err != nil {
			p.freeTurn()
			return nil, err
		}

		if cn == nil {
			break
		}

		// 读请求走replica,只是多一层保护
		if p.cfg.ReadMode == _const.READ_MODE_REPLICA {
			if isReadCmd && cn.remoteType != REMOTE_TYPE_REPLICA {
				continue
			}
			// 写请求不走replica
			if !isReadCmd && cn.remoteType == REMOTE_TYPE_REPLICA {
				continue
			}
		}

		if !p.isHealthyConn(cn) {
			_ = p.CloseConn(cn)
			continue
		}

		atomic.AddUint32(&p.stats.Hits, 1)
		return cn, nil
	}

	atomic.AddUint32(&p.stats.Misses, 1)

	// zhmark 2024/6/18 如果连接池里没有可用的连接,那么新建连接
	newcn, err := p.newConn(ctx, true, isReadCmd)
	if err != nil {
		p.freeTurn()
		return nil, err
	}

	return newcn, nil
}

img

7.1. isHealthyConn内方法解析

// zhmark 2024/7/8 连接关键检查,维护连接池连接健康
func (p *ConnPool) isHealthyConn(cn *Conn) bool {
	now := time.Now()
	// ConnMaxLifetime 默认为0
	if p.cfg.ConnMaxLifetime > 0 && now.Sub(cn.createdAt) >= p.cfg.ConnMaxLifetime {
		return false
	}
	// ConnMaxIdleTime Default is 30 minutes. -1 disables idle timeout check
	if p.cfg.ConnMaxIdleTime > 0 && now.Sub(cn.UsedAt()) >= p.cfg.ConnMaxIdleTime {
		return false
	}

	if connCheck(cn.netConn) != nil {
		return false
	}

	cn.SetUsedAt(now)
	return true
}

7.1.1. 连接使用时长检验

    1. ConnMaxLifetime默认为0,如果配置了ConnMaxLifetime,那么如果当前时间离连接创建时间超过ConnMaxLifetime,则会判定连接为不健康,进而关闭连接

7.1.2. 连接空闲时长检验

    1. ConnMaxIdleTime,默认为30分钟,如果连接超过ConnMaxIdleTime时间未使用,则会判定连接为不健康

7.1.3. 检查底层网络连接状态

func connCheck(conn net.Conn) error {
	// Reset previous timeout.
	_ = conn.SetDeadline(time.Time{})

	sysConn, ok := conn.(syscall.Conn)
	if !ok {
		return nil
	}
	rawConn, err := sysConn.SyscallConn()
	if err != nil {
		return err
	}

	var sysErr error

	if err := rawConn.Read(func(fd uintptr) bool {
		var buf [1]byte
		n, err := syscall.Read(int(fd), buf[:])
		switch {
		case n == 0 && err == nil:
			sysErr = io.EOF
		case n > 0:
			sysErr = errUnexpectedRead
		case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
			sysErr = nil
		default:
			sysErr = err
		}
		return true
	}); err != nil {
		return err
	}

	return sysErr
}

8. 如何实时监控连接池状态

PoolStats

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1911694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT提问提示指南PDF下载经典分享推荐书籍

ChatGPT提问提示指南PDF,在本书的帮助下,您将学习到如何有效地向 ChatGPT 提出问题,以获得更准确和有用的回答。我们希望这本书能够为您提供实用的指南和策略,帮助您更好地与 ChatGPT 交互。 ChatGPT提问提示指南PDF下载 无论您是…

UMI HTTP接口手册

Translate to English 命令行手册: README_CLI.mdHTTP接口手册: README_HTTP.md HTTP接口手册 (本文档仅适用于 Umi-OCR 最新版本。旧版本请查看 Github备份分支 中对应版本的文档。) 基础说明 如上图,必须允许HTT…

git只列出本地分支

git只列出本地分支 git branch --list git强制删除本地分支 git branch -D_error: the branch dlx-test is not fully merged. -CSDN博客文章浏览阅读648次。git branch -d 可以通过: git branch 查看所有本地分支及其名字,然后删除特定分支。git删除远程remote分支…

IDEA如何创建原生maven子模块

文件 -> 新建 -> 新模块 -> Maven ArcheTypeMaven ArcheType界面中的输入框介绍 名称:子模块的名称位置:子模块存放的路径名创建Git仓库:子模块不单独作为一个git仓库,无需勾选JDK:JDK版本号父项:…

【深度学习基础】MacOS PyCharm连接远程服务器

目录 一、需求描述二、建立与服务器的远程连接1. 新版Pycharm的界面有什么不同?2. 创建远程连接3. 建立本地项目与远程服务器项目之间的路径映射4.设置保存自动上传文件 三、设置解释器总结 写在前面,本人用的是Macbook Pro, M3 MAX处理器&am…

设计模式探索:装饰器模式

1. 装饰器模式定义 装饰器模式(Decorator Pattern) 装饰器模式是一种结构型设计模式,允许向一个对象动态添加行为。在不改变类的接口的情况下,装饰器模式在原始类上增加额外的职责,并且支持多个装饰器嵌套使用。 装…

双色球 | python

1. 玩法规则 “双色球”每注投注号码由 6 个红色球号码和 1 个蓝色球号码组成。红色球号码从 1—33 中选择,蓝色球号码从 1—16 中选择。 球的数字匹配数量和颜色决定了是否中奖。 2. 需求 生成本期双色球中奖号码。(注意:1.生成的红球随机有…

Vuforia AR篇(八)— AR塔防上篇

目录 前言一、设置Vuforia AR环境1. 添加AR Camera2. 设置目标图像 二、创建塔防游戏基础1. 导入素材2. 搭建场景3. 创建敌人4. 创建脚本 前言 在增强现实(AR)技术快速发展的今天,Vuforia作为一个强大的AR开发平台,为开发者提供了…

Docker-compse的应用

1 docker-compose # 使用了docker 面临一个比较大的问题,如果一个djagno项目,使用mysql,redis,不要一次性把所有服务都放到一个容器中,每个服务一个容器,批量的管理多个容器,比较难以操作&…

昇思25天学习打卡营第1天|小试牛刀

这里写自昇思25天学习打卡营第1天|小试牛刀定义目录标题 昇思25天学习打卡营第1天学习了初学入门之基本介绍。了解了昇思MindSpore和华为昇腾AI全栈。训练营中的教程丰富,有初学入门、应用实践和量子计算等。学习打卡营是很好的提升自己的机会。 昇腾计算&#xff…

前端图表库G2快速上手

文档地址&#xff1a; https://g2-v3.antv.vision/zh/docs/manual/getting-started/ https://g2.antv.antgroup.com/ 安装&#xff1a; pnpm i antv/g2在vue3中使用&#xff1a; <script setup> import {Chart} from antv/g2; import {onMounted} from "vue"…

生成多个ssh访问不同git

如果&#xff0c;你的git代码仓库&#xff0c;比如说腾讯云coding&#xff0c;通过ssh秘钥访问&#xff0c;一直用的好好的&#xff0c;有一天&#xff0c;你又增加一个aliyun云效的代码仓库&#xff0c;又配置了aliyun云效的秘钥并且&#xff0c;根据aliyun云效的官方文档上传…

【持续集成_03课_Linux部署Sonar+Gogs+Jenkins】

一、通过虚拟机搭建Linux环境-CnetOS 1、安装virtualbox&#xff0c;和Vmware是一样的&#xff0c;只是box更轻量级 1&#xff09;需要注意内存选择&#xff0c;4G 2、启动完成后&#xff0c;需要获取服务器IP地址 命令 ip add 服务器IP地址 通过本地的工具&#xff0c;进…

后端——全局异常处理

一、老办法try-catch 当我们执行一些错误操作导致程序报错时&#xff0c;程序会捕捉到异常报错&#xff0c;这个异常会存在一个Exception对象里 那我们在spring boot工程开发时&#xff0c;当我们执行一个sql查询时报错了&#xff0c;那就会从最底层的Mapper层捕捉到Exceptio…

三相感应电机的建模仿真(3)基于ABC相坐标系Level2 S-Fun以及定子串不对称电抗起动过程仿真分析

1. 概述 2. 三相感应电动机状态方程式 3. 基于Level2 S-Function的仿真模型建立 4. 动态分析实例 5. 总结 6. 参考文献 1. 概述 三相感应电机自然坐标系下的数学模型是一组周期性变系数微分方程(其电感矩阵是转子位置角的函数,转子位置角随时间按正弦规律变化),将其用…

工作手机监管系统销售防飞单保护企业资源

在竞争激烈的市场环境中&#xff0c;企业的每一笔交易都承载着发展的希望与未来的蓝图。然而&#xff0c;飞单现象如同暗流涌动&#xff0c;悄然侵蚀着企业的利润根基&#xff0c;威胁着团队的凝聚力与客户的信任。为了构建更加稳固的业务防线&#xff0c;我们隆重介绍——百川…

Instruct-GS2GS:通过用户指令编辑 GS 三维场景

Paper: Instruct-GS2GS: Editing 3D Gaussian Splats with Instructions Introduction: https://instruct-gs2gs.github.io/ Code: https://github.com/cvachha/instruct-gs2gs Instruct-GS2GS 复用了 Instruct-NeRF2NeRF 1 的架构&#xff0c;将基于 NeRF 的三维场景编辑方法迁…

gpt-4o看图说话-根据图片回答问题

问题&#xff1a;中国的人口老龄化究竟有多严重&#xff1f; 代码下实现如下&#xff1a;&#xff08;直接调用openai的chat接口&#xff09; import os import base64 import requests def encode_image(image_path): """ 对图片文件进行 Base64 编码 输入…

“郑商企航”暑期社会实践赴美丽美艳直播基地开展调研

马常旭文化传媒网讯&#xff08;记者张明辉报道&#xff09;导读&#xff1a;2024 年 7 月 3 日&#xff0c;商学院暑期社会实践团“郑商企航”在河南省郑州市新密市岳村镇美丽美艳直播基地&#xff0c;展开了一场意义非凡的考察活动&#xff0c;团队成员深度调研了直播基地的产…

昇思MindSpore学习总结十二 —— ShuffleNet图像分类

当前案例不支持在GPU设备上静态图模式运行&#xff0c;其他模式运行皆支持。 1、ShuffleNet网络介绍 ShuffleNetV1是旷视科技提出的一种计算高效的CNN模型&#xff0c;和MobileNet, SqueezeNet等一样主要应用在移动端&#xff0c;所以模型的设计目标就是利用有限的计算资源来达…