从http.Transport看连接池的设计

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 本文基于Golang 1.17.6 1.前言 之前萌叔曾在文章 imroc/req 连接池使用须知 提及过Golang标准库net/http提供的连接池http.Transport,但是是浅尝辄止。 本文萌叔想从http.Transport出发来谈谈一个连接池设计应该考虑哪些问题? 2.连接池的功能特征 下图是针对Grpc和Thrift压测结果(见参考资料1)。我么可以看出,长连接相比与短连接相比,QPS大概提升了1倍多。这是因为长连接减少连接建立的所需的3次握手。 要对长连接进行管理,特别是对闲置的长连接进行管理,就不可避免的引入连接池。 特征 需要一个连接时,并不一定真的创建新连接,而是优先尝试从连接池选出空闲连接;如果连接池对应的连接为空,才创建新连接。 销毁并不是真的销毁,而是将使用完毕的连接放回连接池中(逻辑关闭)。 这里引出了几个问题。 Q1:获取连接阶段,我们有没有办法知道从连接池中取出的空闲连接(复用)是有效的,还是无效的? Q2:把使用完毕的连接放回连接池的阶段,空闲连接数量是否要做上限的约束。如果空闲连接数量有上限约束且空闲连接的数量已经达到上限。那么把连接放回连接池的过程,必然需要将之前的某个空闲连接进行关闭,那么按照什么规则选择这个需要关闭的连接。 Q3:放置在连接池中的连接,随着时间的流逝,它可能会变成无效连接(stale)。比如由于Server端定时清理空闲连接。那么为了确保连接池中连接的有效性,是否需要引入定时的检查逻辑? 3.net/http中连接池的实现 net/http中连接池的实现代码在 net/http/transport.go 中 获取连接 Transport.RoundTrip() -> Transport.getConn() 放回连接(逻辑关闭) Response.Body.Close() -> bodyEOFSignal.Close() -> Transport.tryPutIdleConn() 为了约束空闲连接的数量,连接池引入了几个变量: MaxIdleConns MaxIdleConns controls the maximum number of idle (keep-alive) connections across all hosts. 所有host总的最大空闲连接数量,默认值是100 MaxIdleConnsPerHostif non-zero, controls the maximum idle (keep-alive) connections to keep per-host。 针对每个Host能够保持的最大空闲连接数量。默认值是2 这个是一个比较有意思的变量,因为默认情况,所有的HTTP请求都使用同一个连接池,由于MaxIdleConns存在,如果针对某个Host的 连接占用了大量空间,那么针对其它Host的连接可能就没有存储空间了。 MaxConnsPerHost MaxConnsPerHost optionally limits the total number of connections per host, including connections in the dialing, active, and idle states. 默认值是0,表示不限制。 ...

June 20, 2022 · 2 min

Redis-Cluster集群模式下Redis客户端如何获得slot的路由信息

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 我们都知道在Redis-Cluster集群模式下,集群中有18634个slot,slot分布在集群多个实例上,当执行一个Command时,Redis客户端会提取Command中的key 根据下面的算法得出key所属的slot slot=CRC16(key)&16383 在根据客户端中的路由表,找到slot所在的Redis实例 这里的路由表存储的就是 slot -> redis-instance 那么问题来了redis客户端是如何得到这个路由表的呢? 2. 分析 下面以go-redis/redis的代码为例,谈谈Redis客户端如何获取和维护slot路由信息。 2.1 存储结构 type clusterClient struct { opt *ClusterOptions nodes *clusterNodes state *clusterStateHolder // 在这里 cmdsInfoCache *cmdsInfoCache //nolint:structcheck } type clusterState struct { nodes *clusterNodes Masters []*clusterNode Slaves []*clusterNode slots []*clusterSlot // 路由信息存储在这里 generation uint32 createdAt time.Time } type clusterSlot struct { start, end int nodes []*clusterNode } 2.2 命令执行过程 1)通过key计算出对应slot 2)通过路由表查找到对应的node信息 3)向node发送CMD 其实第2步根据slot从clusterState中查询对应clusterNode ...

May 14, 2022 · 2 min