Skip to content

Commit 0433e98

Browse files
Ak-Armyxpunch
andauthored
Better connection pool handling (#2725)
* [fix] etcd config source prefix issue (#2389) * http transport data race issue (#2436) * [fix] #2431 http transport data race issue * [feature] Ability to close connection while receiving. Ability to send messages while receiving. Icreased r channel limit to 100 to more fluently communication. Do not dropp sent request if r channel is full. * [fix] Use pool connection close timeout * [fix] replace Close with private function * [fix] Do not close the transport client twice in stream connection , the transport client is closed in the rpc codec * [fix] tests --------- Co-authored-by: Johnson C <[email protected]>
1 parent 1c6c1ff commit 0433e98

File tree

6 files changed

+103
-43
lines changed

6 files changed

+103
-43
lines changed

client/options.go

+20-9
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ var (
2727
DefaultPoolSize = 100
2828
// DefaultPoolTTL sets the connection pool ttl.
2929
DefaultPoolTTL = time.Minute
30+
// DefaultPoolCloseTimeout sets the connection pool colse timeout.
31+
DefaultPoolCloseTimeout = time.Second
3032
)
3133

3234
// Options are the Client options.
@@ -63,8 +65,9 @@ type Options struct {
6365
Wrappers []Wrapper
6466

6567
// Connection Pool
66-
PoolSize int
67-
PoolTTL time.Duration
68+
PoolSize int
69+
PoolTTL time.Duration
70+
PoolCloseTimeout time.Duration
6871
}
6972

7073
// CallOptions are options used to make calls to a server.
@@ -140,13 +143,14 @@ func NewOptions(options ...Option) Options {
140143
ConnectionTimeout: DefaultConnectionTimeout,
141144
DialTimeout: transport.DefaultDialTimeout,
142145
},
143-
PoolSize: DefaultPoolSize,
144-
PoolTTL: DefaultPoolTTL,
145-
Broker: broker.DefaultBroker,
146-
Selector: selector.DefaultSelector,
147-
Registry: registry.DefaultRegistry,
148-
Transport: transport.DefaultTransport,
149-
Logger: logger.DefaultLogger,
146+
PoolSize: DefaultPoolSize,
147+
PoolTTL: DefaultPoolTTL,
148+
PoolCloseTimeout: DefaultPoolCloseTimeout,
149+
Broker: broker.DefaultBroker,
150+
Selector: selector.DefaultSelector,
151+
Registry: registry.DefaultRegistry,
152+
Transport: transport.DefaultTransport,
153+
Logger: logger.DefaultLogger,
150154
}
151155

152156
for _, o := range options {
@@ -191,6 +195,13 @@ func PoolTTL(d time.Duration) Option {
191195
}
192196
}
193197

198+
// PoolCloseTimeout sets the connection pool close timeout.
199+
func PoolCloseTimeout(d time.Duration) Option {
200+
return func(o *Options) {
201+
o.PoolCloseTimeout = d
202+
}
203+
}
204+
194205
// Registry to find nodes for a given service.
195206
func Registry(r registry.Registry) Option {
196207
return func(o *Options) {

client/rpc_client.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func newRPCClient(opt ...Option) Client {
4646
pool.Size(opts.PoolSize),
4747
pool.TTL(opts.PoolTTL),
4848
pool.Transport(opts.Transport),
49+
pool.CloseTimeout(opts.PoolCloseTimeout),
4950
)
5051

5152
rc := &rpcClient{
@@ -148,7 +149,10 @@ func (r *rpcClient) call(
148149

149150
c, err := r.pool.Get(address, dOpts...)
150151
if err != nil {
151-
return merrors.InternalServerError("go.micro.client", "connection error: %v", err)
152+
if c == nil {
153+
return merrors.InternalServerError("go.micro.client", "connection error: %v", err)
154+
}
155+
logger.Log(log.ErrorLevel, "failed to close pool", err)
152156
}
153157

154158
seq := atomic.AddUint64(&r.seq, 1) - 1

cmd/cmd.go

+8
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,14 @@ func (c *cmd) Before(ctx *cli.Context) error {
565565
clientOpts = append(clientOpts, client.PoolTTL(d))
566566
}
567567

568+
if t := ctx.String("client_pool_close_timeout"); len(t) > 0 {
569+
d, err := time.ParseDuration(t)
570+
if err != nil {
571+
return fmt.Errorf("failed to parse client_pool_close_timeout: %v", t)
572+
}
573+
clientOpts = append(clientOpts, client.PoolCloseTimeout(d))
574+
}
575+
568576
// We have some command line opts for the server.
569577
// Lets set it up
570578
if len(serverOpts) > 0 {

util/pool/default.go

+57-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pool
22

33
import (
4+
"errors"
45
"sync"
56
"time"
67

@@ -12,37 +13,40 @@ import (
1213
type pool struct {
1314
tr transport.Transport
1415

15-
conns map[string][]*poolConn
16-
size int
17-
ttl time.Duration
18-
19-
sync.Mutex
16+
closeTimeout time.Duration
17+
conns map[string][]*poolConn
18+
mu sync.Mutex
19+
size int
20+
ttl time.Duration
2021
}
2122

2223
type poolConn struct {
23-
created time.Time
2424
transport.Client
25-
id string
25+
26+
closeTimeout time.Duration
27+
created time.Time
28+
id string
2629
}
2730

2831
func newPool(options Options) *pool {
2932
return &pool{
30-
size: options.Size,
31-
tr: options.Transport,
32-
ttl: options.TTL,
33-
conns: make(map[string][]*poolConn),
33+
size: options.Size,
34+
tr: options.Transport,
35+
ttl: options.TTL,
36+
closeTimeout: options.CloseTimeout,
37+
conns: make(map[string][]*poolConn),
3438
}
3539
}
3640

3741
func (p *pool) Close() error {
38-
p.Lock()
39-
defer p.Unlock()
42+
p.mu.Lock()
43+
defer p.mu.Unlock()
4044

4145
var err error
4246

4347
for k, c := range p.conns {
4448
for _, conn := range c {
45-
if nerr := conn.Client.Close(); nerr != nil {
49+
if nerr := conn.close(); nerr != nil {
4650
err = nerr
4751
}
4852
}
@@ -67,7 +71,7 @@ func (p *poolConn) Created() time.Time {
6771
}
6872

6973
func (p *pool) Get(addr string, opts ...transport.DialOption) (Conn, error) {
70-
p.Lock()
74+
p.mu.Lock()
7175
conns := p.conns[addr]
7276

7377
// While we have conns check age and then return one
@@ -79,51 +83,77 @@ func (p *pool) Get(addr string, opts ...transport.DialOption) (Conn, error) {
7983

8084
// If conn is old kill it and move on
8185
if d := time.Since(conn.Created()); d > p.ttl {
82-
if err := conn.Client.Close(); err != nil {
83-
p.Unlock()
84-
return nil, err
86+
if err := conn.close(); err != nil {
87+
p.mu.Unlock()
88+
c, errConn := p.newConn(addr, opts)
89+
if errConn != nil {
90+
return nil, errConn
91+
}
92+
return c, err
8593
}
8694

8795
continue
8896
}
8997

9098
// We got a good conn, lets unlock and return it
91-
p.Unlock()
99+
p.mu.Unlock()
92100

93101
return conn, nil
94102
}
95103

96-
p.Unlock()
104+
p.mu.Unlock()
97105

106+
return p.newConn(addr, opts)
107+
}
108+
109+
func (p *pool) newConn(addr string, opts []transport.DialOption) (Conn, error) {
98110
// create new conn
99111
c, err := p.tr.Dial(addr, opts...)
100112
if err != nil {
101113
return nil, err
102114
}
103115

104116
return &poolConn{
105-
Client: c,
106-
id: uuid.New().String(),
107-
created: time.Now(),
117+
Client: c,
118+
id: uuid.New().String(),
119+
closeTimeout: p.closeTimeout,
120+
created: time.Now(),
108121
}, nil
109122
}
110123

111124
func (p *pool) Release(conn Conn, err error) error {
112125
// don't store the conn if it has errored
113126
if err != nil {
114-
return conn.(*poolConn).Client.Close()
127+
return conn.(*poolConn).close()
115128
}
116129

117130
// otherwise put it back for reuse
118-
p.Lock()
119-
defer p.Unlock()
131+
p.mu.Lock()
132+
defer p.mu.Unlock()
120133

121134
conns := p.conns[conn.Remote()]
122135
if len(conns) >= p.size {
123-
return conn.(*poolConn).Client.Close()
136+
return conn.(*poolConn).close()
124137
}
125138

126139
p.conns[conn.Remote()] = append(conns, conn.(*poolConn))
127140

128141
return nil
129142
}
143+
144+
func (p *poolConn) close() error {
145+
ch := make(chan error)
146+
go func() {
147+
defer close(ch)
148+
ch <- p.Client.Close()
149+
}()
150+
t := time.NewTimer(p.closeTimeout)
151+
var err error
152+
select {
153+
case <-t.C:
154+
err = errors.New("unable to close in time")
155+
case err = <-ch:
156+
t.Stop()
157+
}
158+
return err
159+
}

util/pool/default_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ func testPool(t *testing.T, size int, ttl time.Duration) {
7373
// release the conn
7474
p.Release(c, nil)
7575

76-
p.Lock()
76+
p.mu.Lock()
7777
if i := len(p.conns[l.Addr()]); i > size {
78-
p.Unlock()
78+
p.mu.Unlock()
7979
t.Fatalf("pool size %d is greater than expected %d", i, size)
8080
}
81-
p.Unlock()
81+
p.mu.Unlock()
8282
}
8383
}
8484

util/pool/options.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import (
77
)
88

99
type Options struct {
10-
Transport transport.Transport
11-
TTL time.Duration
12-
Size int
10+
Transport transport.Transport
11+
TTL time.Duration
12+
CloseTimeout time.Duration
13+
Size int
1314
}
1415

1516
type Option func(*Options)
@@ -31,3 +32,9 @@ func TTL(t time.Duration) Option {
3132
o.TTL = t
3233
}
3334
}
35+
36+
func CloseTimeout(t time.Duration) Option {
37+
return func(o *Options) {
38+
o.CloseTimeout = t
39+
}
40+
}

0 commit comments

Comments
 (0)