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