grpc client连接池
grpc连接池可以采用sync.Pool实现。 可以有效减少重复创建grpc连接的资源消耗
package client_pool
import (
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"log"
"sync"
)
// 用sync.pool实现
var p sync.Pool
type ClientPool interface {
Get() *grpc.ClientConn
Put(conn *grpc.ClientConn)
}
type clientPool struct {
pool sync.Pool
}
func GetPool(target string, opts ...grpc.DialOption)(ClientPool, error) {
return &clientPool{
pool: sync.Pool{
New: func() any {
conn, err := grpc.Dial(target, opts...)
if err != nil {
log.Println(err)
return nil
}
return conn
},
},
}, nil
}
func (c *clientPool) Get() *grpc.ClientConn {
conn := c.pool.Get().(*grpc.ClientConn)
// 如果连接关闭或者失败
if conn.GetState() == connectivity.Shutdown || conn.GetState() == connectivity.TransientFailure {
conn.Close()
conn = c.pool.New().(*grpc.ClientConn)
}
return conn
}
func (c *clientPool) Put(conn *grpc.ClientConn) {
if conn.GetState() == connectivity.Shutdown || conn.GetState() == connectivity.TransientFailure {
conn.Close()
conn = c.pool.New().(*grpc.ClientConn)
}
c.pool.Put(conn)
}
在使用的时候,由于sync.Pool Get方法会pop处集合中的资源,使用完后需要Put回去
func (p *Pool) Get() any {
if race.Enabled {
race.Disable()
}
l, pid := p.pin()
x := l.private
l.private = nil
if x == nil {
// Try to pop the head of the local shard. We prefer
// the head over the tail for temporal locality of
// reuse.
x, _ = l.shared.popHead()
if x == nil {
x = p.getSlow(pid)
}
}
runtime_procUnpin()
if race.Enabled {
race.Enable()
if x != nil {
race.Acquire(poolRaceAddr(x))
}
}
if x == nil && p.New != nil {
x = p.New()
}
return x
}
main.go
func main() {
flag.Parse()
//conn, err := grpc.Dial(*addr, getDiaOption()...)
// 根据协议+服务名 通过服务名称解析 访问服务器
pool, err := client_pool.GetPool(*addr, getDiaOption()...)
if err != nil {
log.Fatal(err)
}
conn := pool.Get()
// 重复使用连接,不用关闭
defer pool.Put(conn)
c := echo.NewEchoClient(conn)
// grpc调用
}
最后,连接池里的对象只是临时的,golang gc的时候也会释放掉一些不用的对象,连接池的对象也并不是一直存在的。