dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/getty/getty_client.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package getty 19 20 import ( 21 "math/rand" 22 "sync" 23 "time" 24 ) 25 26 import ( 27 getty "github.com/apache/dubbo-getty" 28 29 "github.com/dubbogo/gost/log/logger" 30 gxsync "github.com/dubbogo/gost/sync" 31 gxtime "github.com/dubbogo/gost/time" 32 33 perrors "github.com/pkg/errors" 34 35 "go.uber.org/atomic" 36 37 "gopkg.in/yaml.v2" 38 ) 39 40 import ( 41 "dubbo.apache.org/dubbo-go/v3/common" 42 "dubbo.apache.org/dubbo-go/v3/config" 43 "dubbo.apache.org/dubbo-go/v3/remoting" 44 ) 45 46 var ( 47 errSessionNotExist = perrors.New("session not exist") 48 errClientClosed = perrors.New("client closed") 49 errClientReadTimeout = perrors.New("maybe the client read timeout or fail to decode tcp stream in Writer.Write") 50 51 clientConf *ClientConfig 52 53 clientGrPool gxsync.GenericTaskPool 54 ) 55 56 // it is init client for single protocol. 57 func initClient(protocol string) { 58 clientConf = GetDefaultClientConfig() 59 if protocol == "" { 60 return 61 } 62 63 // load client config from rootConfig.Protocols 64 // default use dubbo 65 if config.GetApplicationConfig() == nil { 66 return 67 } 68 if config.GetRootConfig().Protocols == nil { 69 return 70 } 71 72 protocolConf := config.GetRootConfig().Protocols[protocol] 73 if protocolConf == nil { 74 logger.Info("use default getty client config") 75 return 76 } else { 77 //client tls config 78 tlsConfig := config.GetRootConfig().TLSConfig 79 if tlsConfig != nil { 80 clientConf.SSLEnabled = true 81 clientConf.TLSBuilder = &getty.ClientTlsConfigBuilder{ 82 ClientKeyCertChainPath: tlsConfig.TLSCertFile, 83 ClientPrivateKeyPath: tlsConfig.TLSKeyFile, 84 ClientTrustCertCollectionPath: tlsConfig.CACertFile, 85 } 86 } 87 //getty params 88 gettyClientConfig := protocolConf.Params 89 if gettyClientConfig == nil { 90 logger.Debugf("gettyClientConfig is nil") 91 return 92 } 93 gettyClientConfigBytes, err := yaml.Marshal(gettyClientConfig) 94 if err != nil { 95 panic(err) 96 } 97 err = yaml.Unmarshal(gettyClientConfigBytes, clientConf) 98 if err != nil { 99 panic(err) 100 } 101 } 102 if err := clientConf.CheckValidity(); err != nil { 103 logger.Warnf("[CheckValidity] error: %v", err) 104 return 105 } 106 setClientGrPool() 107 108 rand.Seed(time.Now().UnixNano()) 109 } 110 111 // SetClientConf ClientConf 112 func SetClientConf(c ClientConfig) { 113 clientConf = &c 114 err := clientConf.CheckValidity() 115 if err != nil { 116 logger.Warnf("[ClientConfig CheckValidity] error: %v", err) 117 return 118 } 119 setClientGrPool() 120 } 121 122 func setClientGrPool() { 123 clientGrPool = gxsync.NewTaskPoolSimple(clientConf.GrPoolSize) 124 } 125 126 // Options : param config 127 type Options struct { 128 ConnectTimeout time.Duration 129 RequestTimeout time.Duration 130 } 131 132 // Client : some configuration for network communication. 133 type Client struct { 134 addr string 135 opts Options 136 conf ClientConfig 137 mux sync.RWMutex 138 sslEnabled bool 139 clientClosed bool 140 gettyClient *gettyRPCClient 141 gettyClientMux sync.RWMutex 142 gettyClientCreated atomic.Bool 143 codec remoting.Codec 144 } 145 146 // NewClient create client 147 func NewClient(opt Options) *Client { 148 switch { 149 case opt.ConnectTimeout == 0: 150 opt.ConnectTimeout = 3 * time.Second 151 fallthrough 152 case opt.RequestTimeout == 0: 153 opt.RequestTimeout = 3 * time.Second 154 } 155 156 c := &Client{ 157 opts: opt, 158 clientClosed: false, 159 } 160 c.gettyClientCreated.Store(false) 161 return c 162 } 163 164 func (c *Client) SetExchangeClient(client *remoting.ExchangeClient) { 165 } 166 167 // Connect init client and try to connection. 168 func (c *Client) Connect(url *common.URL) error { 169 initClient(url.Protocol) 170 c.conf = *clientConf 171 c.sslEnabled = c.conf.SSLEnabled 172 // codec 173 c.codec = remoting.GetCodec(url.Protocol) 174 c.addr = url.Location 175 _, _, err := c.selectSession(c.addr) 176 if err != nil { 177 logger.Errorf("try to connect server %v failed for : %v", url.Location, err) 178 } 179 return err 180 } 181 182 // Close close network connection 183 func (c *Client) Close() { 184 c.mux.Lock() 185 client := c.gettyClient 186 c.gettyClient = nil 187 c.clientClosed = true 188 c.mux.Unlock() 189 if client != nil { 190 client.close() 191 } 192 } 193 194 // Request send request 195 func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error { 196 _, session, err := c.selectSession(c.addr) 197 if err != nil { 198 return perrors.WithStack(err) 199 } 200 if session == nil { 201 return errSessionNotExist 202 } 203 var ( 204 totalLen int 205 sendLen int 206 ) 207 if totalLen, sendLen, err = c.transfer(session, request, timeout); err != nil { 208 if sendLen != 0 && totalLen != sendLen { 209 logger.Warnf("start to close the session at request because %d of %d bytes data is sent success. err:%+v", sendLen, totalLen, err) 210 go c.Close() 211 } 212 return perrors.WithStack(err) 213 } 214 215 if !request.TwoWay || response.Callback != nil { 216 return nil 217 } 218 219 select { 220 case <-gxtime.After(timeout): 221 return perrors.WithStack(errClientReadTimeout) 222 case <-response.Done: 223 err = response.Err 224 } 225 226 return perrors.WithStack(err) 227 } 228 229 // IsAvailable returns true if the connection is available, or it can be re-established. 230 func (c *Client) IsAvailable() bool { 231 client, _, err := c.selectSession(c.addr) 232 return err == nil && 233 // defensive check 234 client != nil 235 } 236 237 func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) { 238 c.mux.RLock() 239 defer c.mux.RUnlock() 240 if c.clientClosed { 241 return nil, nil, perrors.New("client have been closed") 242 } 243 244 if !c.gettyClientCreated.Load() { 245 c.gettyClientMux.Lock() 246 if c.gettyClient == nil { 247 rpcClientConn, rpcErr := newGettyRPCClientConn(c, addr) 248 if rpcErr != nil { 249 c.gettyClientMux.Unlock() 250 return nil, nil, perrors.WithStack(rpcErr) 251 } 252 c.gettyClientCreated.Store(true) 253 c.gettyClient = rpcClientConn 254 } 255 client := c.gettyClient 256 session := c.gettyClient.selectSession() 257 c.gettyClientMux.Unlock() 258 return client, session, nil 259 } 260 c.gettyClientMux.RLock() 261 client := c.gettyClient 262 session := c.gettyClient.selectSession() 263 c.gettyClientMux.RUnlock() 264 return client, session, nil 265 266 } 267 268 func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration) (int, int, error) { 269 totalLen, sendLen, err := session.WritePkg(request, timeout) 270 return totalLen, sendLen, perrors.WithStack(err) 271 } 272 273 func (c *Client) resetRpcConn() { 274 c.gettyClientMux.Lock() 275 c.gettyClient = nil 276 c.gettyClientCreated.Store(false) 277 c.gettyClientMux.Unlock() 278 279 }