gRPC 连接池的一种实现方案

Google远程过程调用(Google Remote Procedure Call,gRPC)是基于HTTP 2.0传输层协议承载的高性能开源 RPC 软件框架,为管理和配置网络设备提供了一种 API 接口设计的方法。gRPC提供了多种编程语言,如C、Java、golong、python等。 gRPC可以作为数据传输协议与Telemetry技术配合使用,可实时、高速、精确的监控网络设备的运行状态。此外,网络设备提供了一种基于 gRPC 方式来管理设备的方法,包括配置、查询和能力获取三种方法。这些方法是通过设备和采集器对接,实现采集设备数据的功能。

本文将详细说明在生产环境已经在使用的一种 gRPC 连接池实现方案,它有一个前提是对 gRPC 的使用已经引入了管理者角色(如 go-micro 框架),而不是完全使用 gRPC 框架管理连接当然,小编也基于 gRPC 的 picker 等实现了原生 gRPC 的连接池管理。


Go-micro 早期版本,本质上是使用了 gRPC 的 transport,而且将 HTTP2.0 的能力退化成了普通的 TCP 连接,每个连接同一时刻只能处理一个请求,并发高的系统对于资源的开销也明显加大。本文的方案就是针对此进行的优化。


整体思路

go-micro 每个请求都会向 manager 获取一个连接,然后进行 invoke 操作。这让我们的优化目标非常明确:如何从 manager 中拿出重复使用,且有效的连接?

查找连接的整体思路为:

  1. 按条件查找有效连接,条件后续介绍;
  2. 如果找到有效连接则直接返回;
  3. 如果没有找到连接则开始争抢创建新连接的门票,防止连接创建过多;
  4. 抢到创建连接门票的进行二次 check,因为此时可能已经有返还使用的连接了;
  5. 如果二次 check 失败则创建新连接并返回连接,此时可考虑配合创建 gRPC 参数是 block 还是非 block 来决定是否返回门票。
image-20220912130158950

查找连接

  1. 从连接中找到状态有效的连接:

    • 连接池未满状态下,连接上的请求计数小于 8(可为其它值),如果连接池已经满,则无此限制;要注意的是 gRPC 有流上限,所以有可能出错原因不是连接池问题,而是 gRPC 限制。

    • 连接未被其它请求置为无效状态;

      连接复用,连接中任何一个请求失败都会主动设置当前连接为无效状态,并从连接管理数组中移除。

      没有直接删除、关闭连接,而是使用引用计数的形式,让最后一个请求来处理连接。

    • 连接在时效内;

  2. 找到有效连接后,增加引用计数并返回;

  3. 找到有效连接后,修改连接所在位置,参与下一次轮询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
func (m *poolManager) isValid(conn *ClientConn) connState {
// 无效 1
if conn == nil {
return invalid
}

// 无效 2
// 已经从真实数据中移除
if _, ok := m.data[conn]; !ok {
return invalid
}

// 无效 3
// 已经过期
if int64(time.Since(conn.created).Seconds()) >= m.ttl {
return invalid
}

// 无效 4
// 如果连接状态都不可用了则直接是无效连接
if conn.GetState() != connectivity.Ready {
return invalid
}

// ===================================================================
// 有效 1
// 如果池子已经到达了上限了,那么只要此连接状态是可用的,就认为是有效的连接
if len(m.data) >= m.size {
return poolFull
}
// 有效 2
// 如果池子还没有满,但是当前连接已经到达了每个连接上承载的请求数时,认为些连接无效
if conn.refCount < requestPerConn {
return lessThanRef
} else {
return moreThanRef
}
// ===================================================================

// 其它情况均为无效
return invalid
}

原生 gRPC 复制多份连接

对于原生 gRPC,是基于不同 node 创建了单独的连接,但是可以修改连接中的 attribute 属性来为每个 node 复制多条连接,防止一条连接上的请求过多。

https://github.com/gowins/go-kit

扫码_搜索联合传播样式-标准色版

gRPC 连接池的一种实现方案
https://blog.isnap.cn/posts/f227003e/
作者
三岁于辛
发布于
2023年2月19日
许可协议