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  }