github.com/cloudwego/kitex@v0.9.0/pkg/remote/remotecli/conn_wrapper.go (about) 1 /* 2 * Copyright 2021 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package remotecli 18 19 import ( 20 "context" 21 "net" 22 "sync" 23 "time" 24 25 "github.com/cloudwego/kitex/pkg/kerrors" 26 "github.com/cloudwego/kitex/pkg/remote" 27 "github.com/cloudwego/kitex/pkg/rpcinfo" 28 "github.com/cloudwego/kitex/pkg/stats" 29 ) 30 31 var connWrapperPool sync.Pool 32 33 func init() { 34 connWrapperPool.New = newConnWrapper 35 } 36 37 // ConnWrapper wraps a connection. 38 type ConnWrapper struct { 39 connPool remote.ConnPool 40 conn net.Conn 41 } 42 43 // NewConnWrapper returns a new ConnWrapper using the given connPool and logger. 44 func NewConnWrapper(connPool remote.ConnPool) *ConnWrapper { 45 cm := connWrapperPool.Get().(*ConnWrapper) 46 cm.connPool = connPool 47 return cm 48 } 49 50 // GetConn returns a connection using the given Dialer and RPCInfo. 51 func (cm *ConnWrapper) GetConn(ctx context.Context, d remote.Dialer, ri rpcinfo.RPCInfo) (net.Conn, error) { 52 configs := ri.Config() 53 timeout := configs.ConnectTimeout() 54 if cm.connPool != nil { 55 conn, err := cm.getConnWithPool(ctx, cm.connPool, d, timeout, ri) 56 if err != nil { 57 return nil, err 58 } 59 cm.conn = conn 60 if raw, ok := conn.(remote.RawConn); ok { 61 rawConn := raw.RawConn() 62 return rawConn, nil 63 } 64 return cm.conn, nil 65 } 66 67 var err error 68 cm.conn, err = cm.getConnWithDialer(ctx, d, timeout, ri) 69 return cm.conn, err 70 } 71 72 // ReleaseConn should notice that ri may nil when oneway 73 // TODO duplicate release may cause problem? 74 func (cm *ConnWrapper) ReleaseConn(err error, ri rpcinfo.RPCInfo) { 75 if cm.conn == nil { 76 return 77 } 78 if cm.connPool != nil { 79 if err == nil { 80 _, ok := ri.To().Tag(rpcinfo.ConnResetTag) 81 if ok || ri.Config().InteractionMode() == rpcinfo.Oneway { 82 cm.connPool.Discard(cm.conn) 83 } else { 84 cm.connPool.Put(cm.conn) 85 } 86 } else { 87 cm.connPool.Discard(cm.conn) 88 } 89 } else { 90 cm.conn.Close() 91 } 92 93 cm.zero() 94 connWrapperPool.Put(cm) 95 } 96 97 func newConnWrapper() interface{} { 98 return &ConnWrapper{} 99 } 100 101 func (cm *ConnWrapper) zero() { 102 cm.connPool = nil 103 cm.conn = nil 104 } 105 106 func (cm *ConnWrapper) getConnWithPool(ctx context.Context, cp remote.ConnPool, d remote.Dialer, 107 timeout time.Duration, ri rpcinfo.RPCInfo, 108 ) (net.Conn, error) { 109 addr := ri.To().Address() 110 if addr == nil { 111 return nil, kerrors.ErrNoDestAddress 112 } 113 opt := remote.ConnOption{Dialer: d, ConnectTimeout: timeout} 114 ri.Stats().Record(ctx, stats.ClientConnStart, stats.StatusInfo, "") 115 conn, err := cp.Get(ctx, addr.Network(), addr.String(), opt) 116 if err != nil { 117 ri.Stats().Record(ctx, stats.ClientConnFinish, stats.StatusError, err.Error()) 118 return nil, kerrors.ErrGetConnection.WithCause(err) 119 } 120 ri.Stats().Record(ctx, stats.ClientConnFinish, stats.StatusInfo, "") 121 return conn, nil 122 } 123 124 func (cm *ConnWrapper) getConnWithDialer(ctx context.Context, d remote.Dialer, 125 timeout time.Duration, ri rpcinfo.RPCInfo, 126 ) (net.Conn, error) { 127 addr := ri.To().Address() 128 if addr == nil { 129 return nil, kerrors.ErrNoDestAddress 130 } 131 132 ri.Stats().Record(ctx, stats.ClientConnStart, stats.StatusInfo, "") 133 conn, err := d.DialTimeout(addr.Network(), addr.String(), timeout) 134 if err != nil { 135 ri.Stats().Record(ctx, stats.ClientConnFinish, stats.StatusError, err.Error()) 136 return nil, kerrors.ErrGetConnection.WithCause(err) 137 } 138 ri.Stats().Record(ctx, stats.ClientConnFinish, stats.StatusInfo, "") 139 return conn, nil 140 }