go-micro.dev/v5@v5.12.0/client/grpc/grpc_pool.go (about) 1 package grpc 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "google.golang.org/grpc" 9 "google.golang.org/grpc/connectivity" 10 ) 11 12 type pool struct { 13 size int 14 ttl int64 15 16 // max streams on a *poolConn 17 maxStreams int 18 // max idle conns 19 maxIdle int 20 21 sync.Mutex 22 conns map[string]*streamsPool 23 } 24 25 type streamsPool struct { 26 // head of list 27 head *poolConn 28 // busy conns list 29 busy *poolConn 30 // the size of list 31 count int 32 // idle conn 33 idle int 34 } 35 36 type poolConn struct { 37 // grpc conn 38 *grpc.ClientConn 39 err error 40 addr string 41 42 // pool and streams pool 43 pool *pool 44 sp *streamsPool 45 streams int 46 created int64 47 48 // list 49 pre *poolConn 50 next *poolConn 51 in bool 52 } 53 54 func newPool(size int, ttl time.Duration, idle int, ms int) *pool { 55 if ms <= 0 { 56 ms = 1 57 } 58 if idle < 0 { 59 idle = 0 60 } 61 return &pool{ 62 size: size, 63 ttl: int64(ttl.Seconds()), 64 maxStreams: ms, 65 maxIdle: idle, 66 conns: make(map[string]*streamsPool), 67 } 68 } 69 70 func (p *pool) getConn(dialCtx context.Context, addr string, opts ...grpc.DialOption) (*poolConn, error) { 71 now := time.Now().Unix() 72 p.Lock() 73 sp, ok := p.conns[addr] 74 if !ok { 75 sp = &streamsPool{head: &poolConn{}, busy: &poolConn{}, count: 0, idle: 0} 76 p.conns[addr] = sp 77 } 78 // while we have conns check streams and then return one 79 // otherwise we'll create a new conn 80 conn := sp.head.next 81 for conn != nil { 82 // check conn state 83 // https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md 84 switch conn.GetState() { 85 case connectivity.Connecting: 86 conn = conn.next 87 continue 88 case connectivity.Shutdown: 89 next := conn.next 90 if conn.streams == 0 { 91 removeConn(conn) 92 sp.idle-- 93 } 94 conn = next 95 continue 96 case connectivity.TransientFailure: 97 next := conn.next 98 if conn.streams == 0 { 99 removeConn(conn) 100 conn.ClientConn.Close() 101 sp.idle-- 102 } 103 conn = next 104 continue 105 case connectivity.Ready: 106 case connectivity.Idle: 107 } 108 // a old conn 109 if now-conn.created > p.ttl { 110 next := conn.next 111 if conn.streams == 0 { 112 removeConn(conn) 113 conn.ClientConn.Close() 114 sp.idle-- 115 } 116 conn = next 117 continue 118 } 119 // a busy conn 120 if conn.streams >= p.maxStreams { 121 next := conn.next 122 removeConn(conn) 123 addConnAfter(conn, sp.busy) 124 conn = next 125 continue 126 } 127 // a idle conn 128 if conn.streams == 0 { 129 sp.idle-- 130 } 131 // a good conn 132 conn.streams++ 133 p.Unlock() 134 return conn, nil 135 } 136 p.Unlock() 137 138 // create new conn 139 cc, err := grpc.DialContext(dialCtx, addr, opts...) 140 if err != nil { 141 return nil, err 142 } 143 conn = &poolConn{cc, nil, addr, p, sp, 1, time.Now().Unix(), nil, nil, false} 144 145 // add conn to streams pool 146 p.Lock() 147 if sp.count < p.size { 148 addConnAfter(conn, sp.head) 149 } 150 p.Unlock() 151 152 return conn, nil 153 } 154 155 func (p *pool) release(addr string, conn *poolConn, err error) { 156 p.Lock() 157 p, sp, created := conn.pool, conn.sp, conn.created 158 // try to add conn 159 if !conn.in && sp.count < p.size { 160 addConnAfter(conn, sp.head) 161 } 162 if !conn.in { 163 p.Unlock() 164 conn.ClientConn.Close() 165 return 166 } 167 // a busy conn 168 if conn.streams >= p.maxStreams { 169 removeConn(conn) 170 addConnAfter(conn, sp.head) 171 } 172 conn.streams-- 173 // if streams == 0, we can do something 174 if conn.streams == 0 { 175 // 1. it has errored 176 // 2. too many idle conn or 177 // 3. conn is too old 178 now := time.Now().Unix() 179 if err != nil || sp.idle >= p.maxIdle || now-created > p.ttl { 180 removeConn(conn) 181 p.Unlock() 182 conn.ClientConn.Close() 183 return 184 } 185 sp.idle++ 186 } 187 p.Unlock() 188 } 189 190 func (conn *poolConn) Close() { 191 conn.pool.release(conn.addr, conn, conn.err) 192 } 193 194 func removeConn(conn *poolConn) { 195 if conn.pre != nil { 196 conn.pre.next = conn.next 197 } 198 if conn.next != nil { 199 conn.next.pre = conn.pre 200 } 201 conn.pre = nil 202 conn.next = nil 203 conn.in = false 204 conn.sp.count-- 205 return 206 } 207 208 func addConnAfter(conn *poolConn, after *poolConn) { 209 conn.next = after.next 210 conn.pre = after 211 if after.next != nil { 212 after.next.pre = conn 213 } 214 after.next = conn 215 conn.in = true 216 conn.sp.count++ 217 return 218 }