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