@@ -23,7 +23,6 @@ import (
2323 "runtime"
2424 "runtime/debug"
2525 "sync"
26- "sync/atomic"
2726 "time"
2827
2928 "golang.org/x/sync/singleflight"
@@ -60,7 +59,7 @@ func NewConnPool(remoteService string, size uint32, connOpts grpc.ConnectOptions
6059 }
6160}
6261
63- // MuxPool manages a pool of long connections.
62+ // connPool manages a pool of gRPC long connections.
6463type connPool struct {
6564 size uint32
6665 sfg singleflight.Group
@@ -69,76 +68,8 @@ type connPool struct {
6968 connOpts grpc.ConnectOptions
7069}
7170
72- type transports struct {
73- index uint32
74- size uint32
75- cliTransports []grpc.ClientTransport
76- }
77-
78- // get connection from the pool, load balance with round-robin.
79- func (t * transports ) get () grpc.ClientTransport {
80- idx := atomic .AddUint32 (& t .index , 1 )
81- return t .cliTransports [idx % t .size ]
82- }
83-
84- // put find the first empty position to put the connection to the pool.
85- func (t * transports ) put (trans grpc.ClientTransport ) {
86- for i := 0 ; i < int (t .size ); i ++ {
87- cliTransport := t .cliTransports [i ]
88- if cliTransport == nil {
89- t .cliTransports [i ] = trans
90- return
91- }
92- if ! cliTransport .(grpc.IsActive ).IsActive () {
93- t .cliTransports [i ].GracefulClose ()
94- t .cliTransports [i ] = trans
95- return
96- }
97- }
98- }
99-
100- // close all connections of the pool.
101- func (t * transports ) close () {
102- for i := range t .cliTransports {
103- if c := t .cliTransports [i ]; c != nil {
104- c .GracefulClose ()
105- }
106- }
107- }
108-
10971var _ remote.LongConnPool = (* connPool )(nil )
11072
111- func (p * connPool ) newTransport (ctx context.Context , dialer remote.Dialer , network , address string ,
112- connectTimeout time.Duration , opts grpc.ConnectOptions ,
113- ) (grpc.ClientTransport , error ) {
114- conn , err := dialer .DialTimeout (network , address , connectTimeout )
115- if err != nil {
116- return nil , err
117- }
118- if opts .TLSConfig != nil {
119- tlsConn , err := newTLSConn (conn , opts .TLSConfig )
120- if err != nil {
121- return nil , err
122- }
123- conn = tlsConn
124- }
125- return grpc .NewClientTransport (
126- ctx ,
127- conn ,
128- opts ,
129- p .remoteService ,
130- func (grpc.GoAwayReason ) {
131- // remove connection from the pool.
132- // we do not need to close this grpc transport manually
133- // since grpc client is responsible for doing this.
134- p .conns .Delete (address )
135- },
136- func () {
137- // do nothing
138- },
139- )
140- }
141-
14273// Get pick or generate a net.Conn and return
14374func (p * connPool ) Get (ctx context.Context , network , address string , opt remote.ConnOption ) (net.Conn , error ) {
14475 if p .connOpts .ShortConn {
@@ -147,47 +78,47 @@ func (p *connPool) Get(ctx context.Context, network, address string, opt remote.
14778
14879 var (
14980 trans * transports
81+ tr grpc.ClientTransport
82+ idx uint32
15083 conn * clientConn
15184 err error
15285 )
15386
15487 v , ok := p .conns .Load (address )
15588 if ok {
15689 trans = v .(* transports )
157- if tr := trans .get (); tr != nil {
158- if tr .(grpc.IsActive ).IsActive () {
159- // Actually new a stream, reuse the connection (grpc.ClientTransport)
160- conn , err = newClientConn (ctx , tr , address )
161- if err == nil {
162- return conn , nil
163- }
164- klog .CtxDebugf (ctx , "KITEX: New grpc stream failed, network=%s, address=%s, error=%s" , network , address , err .Error ())
90+ tr , idx = trans .getActiveTransport ()
91+ if tr != nil {
92+ // Actually new a stream, reuse the connection (grpc.ClientTransport)
93+ conn , err = newClientConn (ctx , tr , address )
94+ if err == nil {
95+ return conn , nil
16596 }
97+ klog .CtxDebugf (ctx , "KITEX: New grpc stream failed, network=%s, address=%s, error=%s" , network , address , err .Error ())
16698 }
16799 }
168- tr , err , _ := p .sfg .Do (address , func () (i interface {}, e error ) {
169- // Notice: newTransport means new a connection, the timeout of connection cannot be set,
170- // so using context.Background() but not the ctx passed in as the parameter.
171- tr , err := p . newTransport ( context . Background (), opt . Dialer , network , address , opt . ConnectTimeout , p . connOpts )
172- if err ! = nil {
173- return nil , err
100+ rawTr , dErr , _ := p .sfg .Do (address , func () (i interface {}, e error ) {
101+ // avoid creating duplicate transports
102+ if existTrans , ok := p . conns . Load ( address ); ok {
103+ trans = existTrans .( * transports )
104+ } else if trans = = nil {
105+ trans = newTransports ( p . size )
174106 }
175- if trans == nil {
176- trans = & transports {
177- size : p .size ,
178- cliTransports : make ([]grpc.ClientTransport , p .size ),
179- }
107+
108+ res , cErr := trans .createTransport (idx , p .remoteService , opt .Dialer , network , address , opt .ConnectTimeout , p .connOpts )
109+ if cErr != nil {
110+ return nil , cErr
180111 }
181- trans . put ( tr ) // the tr (connection) maybe not in the pool, but can be recycled by keepalive.
182- p .conns .Store (address , trans )
183- return tr , nil
112+
113+ p .conns .LoadOrStore (address , trans )
114+ return res , nil
184115 })
185- if err != nil {
186- klog .CtxErrorf (ctx , "KITEX: New grpc client connection failed, network=%s, address=%s, error=%s" , network , address , err .Error ())
187- return nil , err
116+ if dErr != nil {
117+ klog .CtxErrorf (ctx , "KITEX: New grpc client connection failed, network=%s, address=%s, error=%s" , network , address , dErr .Error ())
118+ return nil , dErr
188119 }
189120 klog .CtxDebugf (ctx , "KITEX: New grpc client connection succeed, network=%s, address=%s" , network , address )
190- return newClientConn (ctx , tr .(grpc.ClientTransport ), address )
121+ return newClientConn (ctx , rawTr .(grpc.ClientTransport ), address )
191122}
192123
193124// Put implements the ConnPool interface.
@@ -205,9 +136,7 @@ func (p *connPool) release(conn net.Conn) error {
205136}
206137
207138func (p * connPool ) createShortConn (ctx context.Context , network , address string , opt remote.ConnOption ) (net.Conn , error ) {
208- // Notice: newTransport means new a connection, the timeout of connection cannot be set,
209- // so using context.Background() but not the ctx passed in as the parameter.
210- tr , err := p .newTransport (context .Background (), opt .Dialer , network , address , opt .ConnectTimeout , p .connOpts )
139+ tr , err := newTransport (p .remoteService , opt .Dialer , network , address , opt .ConnectTimeout , p .connOpts , nil , nil )
211140 if err != nil {
212141 return nil , err
213142 }
@@ -224,8 +153,7 @@ func (p *connPool) Discard(conn net.Conn) error {
224153
225154// Clean implements the LongConnPool interface.
226155func (p * connPool ) Clean (network , address string ) {
227- if v , ok := p .conns .Load (address ); ok {
228- p .conns .Delete (address )
156+ if v , ok := p .conns .LoadAndDelete (address ); ok {
229157 v .(* transports ).close ()
230158 }
231159}
@@ -240,6 +168,11 @@ func (p *connPool) Close() error {
240168 return nil
241169}
242170
171+ type dumpEntry struct {
172+ addr string
173+ tr grpc.ClientTransport
174+ }
175+
243176// Dump dumps the connection pool with the details of the underlying transport.
244177func (p * connPool ) Dump () interface {} {
245178 defer func () {
@@ -252,31 +185,61 @@ func (p *connPool) Dump() interface{} {
252185 // If mesh egress is not enabled, toAddr should be the address of the callee service.
253186 // Otherwise, toAddr will be the same, so you should check the remoteAddress in each stream, which is read from the header.
254187 poolDump := make (map [string ]interface {}, p .size )
188+ var cliTransDumps []dumpEntry
255189 p .conns .Range (func (k , v interface {}) bool {
256190 addr := k .(string )
257- ts := v .(* transports )
258- for _ , t := range ts .cliTransports {
259- if t == nil {
260- continue
261- }
262- dumper , ok := t .(interface { Dump () interface {} })
263- if ! ok {
264- continue
265- }
266- var curr []interface {}
267- if poolDump [addr ] == nil {
268- curr = make ([]interface {}, 0 )
269- } else {
270- curr = poolDump [addr ].([]interface {})
271- }
272- curr = append (curr , dumper .Dump ())
273- poolDump [addr ] = curr
191+ for _ , tr := range v .(* transports ).loadAll () {
192+ cliTransDumps = append (cliTransDumps , dumpEntry {addr : addr , tr : tr })
274193 }
275194 return true
276195 })
196+
197+ for _ , cliTransDump := range cliTransDumps {
198+ dumper , ok := cliTransDump .tr .(interface { Dump () interface {} })
199+ if ! ok {
200+ continue
201+ }
202+ var curr []interface {}
203+ if poolDump [cliTransDump .addr ] == nil {
204+ curr = make ([]interface {}, 0 )
205+ } else {
206+ curr = poolDump [cliTransDump .addr ].([]interface {})
207+ }
208+ curr = append (curr , dumper .Dump ())
209+ poolDump [cliTransDump .addr ] = curr
210+ }
277211 return poolDump
278212}
279213
214+ // newTransport created a gRPC connection
215+ func newTransport (remoteService string ,
216+ dialer remote.Dialer , network , address string , connectTimeout time.Duration , opts grpc.ConnectOptions ,
217+ onGoAway func (context.Context , grpc.ClientTransport , grpc.GoAwayReason ),
218+ onClose func (context.Context , grpc.ClientTransport , error ),
219+ ) (grpc.ClientTransport , error ) {
220+ conn , err := dialer .DialTimeout (network , address , connectTimeout )
221+ if err != nil {
222+ return nil , err
223+ }
224+ if opts .TLSConfig != nil {
225+ tlsConn , err := newTLSConn (conn , opts .TLSConfig )
226+ if err != nil {
227+ return nil , err
228+ }
229+ conn = tlsConn
230+ }
231+ return grpc .NewClientTransportWithConfig (
232+ context .Background (), // gRPC connection does not need to be bound to a specific ctx
233+ conn ,
234+ opts ,
235+ grpc.ClientConfig {
236+ RemoteService : remoteService ,
237+ OnGoAway : onGoAway ,
238+ OnClose : onClose ,
239+ },
240+ )
241+ }
242+
280243// newTLSConn constructs a client-side TLS connection and performs handshake.
281244func newTLSConn (conn net.Conn , tlsCfg * tls.Config ) (net.Conn , error ) {
282245 tlsConn := tls .Client (conn , tlsCfg )
@@ -285,3 +248,12 @@ func newTLSConn(conn net.Conn, tlsCfg *tls.Config) (net.Conn, error) {
285248 }
286249 return tlsConn , nil
287250}
251+
252+ func checkActive (trans grpc.ClientTransport ) bool {
253+ if trans == nil {
254+ return false
255+ }
256+ // grpc.ClientTransport is implemented by *http2Client in pkg/remote/trans/nphttp2/grpc
257+ // it has implements grpc.IsActive so we do not assert type
258+ return trans .(grpc.IsActive ).IsActive ()
259+ }
0 commit comments