dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/exchange_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 remoting
    19  
    20  import (
    21  	"errors"
    22  	"time"
    23  )
    24  
    25  import (
    26  	"github.com/dubbogo/gost/log/logger"
    27  
    28  	uatomic "go.uber.org/atomic"
    29  )
    30  
    31  import (
    32  	"dubbo.apache.org/dubbo-go/v3/common"
    33  	"dubbo.apache.org/dubbo-go/v3/protocol"
    34  )
    35  
    36  // Client is the interface that wraps SetExchangeClient、 Connect、Close、Request and
    37  // IsAvailable method. It is interface of client for network communication. If you
    38  // use getty as network communication, you should define GettyClient that implements
    39  // this interface.
    40  //
    41  // SetExchangeClient method sets a ExchangeClient instance.
    42  //
    43  // Connect method is to connect url.
    44  //
    45  // Close method is for destroy.
    46  //
    47  // Request method is sending request to server.
    48  //
    49  // IsAvailable method checks whether the client is still available.
    50  type Client interface {
    51  	SetExchangeClient(client *ExchangeClient)
    52  	Connect(url *common.URL) error
    53  	Close()
    54  	Request(request *Request, timeout time.Duration, response *PendingResponse) error
    55  	IsAvailable() bool
    56  }
    57  
    58  // ExchangeClient is abstraction level. it is like facade.
    59  type ExchangeClient struct {
    60  	ConnectTimeout time.Duration  // timeout for connecting server
    61  	address        string         // server address for dialing. The format: ip:port
    62  	client         Client         // dealing with the transport
    63  	init           bool           // the tag for init.
    64  	activeNum      uatomic.Uint32 // the number of service using the exchangeClient
    65  }
    66  
    67  // NewExchangeClient returns a ExchangeClient.
    68  func NewExchangeClient(url *common.URL, client Client, connectTimeout time.Duration, lazyInit bool) *ExchangeClient {
    69  	exchangeClient := &ExchangeClient{
    70  		ConnectTimeout: connectTimeout,
    71  		address:        url.Location,
    72  		client:         client,
    73  	}
    74  	if !lazyInit {
    75  		if err := exchangeClient.doInit(url); err != nil {
    76  			return nil
    77  		}
    78  	}
    79  	exchangeClient.IncreaseActiveNumber()
    80  	return exchangeClient
    81  }
    82  
    83  func (cl *ExchangeClient) doInit(url *common.URL) error {
    84  	if cl.init {
    85  		return nil
    86  	}
    87  	if cl.client.Connect(url) != nil {
    88  		// retry for a while
    89  		time.Sleep(100 * time.Millisecond)
    90  		if cl.client.Connect(url) != nil {
    91  			logger.Errorf("Failed to connect server %+v " + url.Location)
    92  			return errors.New("Failed to connect server " + url.Location)
    93  		}
    94  	}
    95  	// FIXME atomic operation
    96  	cl.init = true
    97  	return nil
    98  }
    99  
   100  // IncreaseActiveNumber increase number of service using client.
   101  func (client *ExchangeClient) IncreaseActiveNumber() uint32 {
   102  	return client.activeNum.Add(1)
   103  }
   104  
   105  // DecreaseActiveNumber decrease number of service using client.
   106  func (client *ExchangeClient) DecreaseActiveNumber() uint32 {
   107  	return client.activeNum.Sub(1)
   108  }
   109  
   110  // GetActiveNumber get number of service using client.
   111  func (client *ExchangeClient) GetActiveNumber() uint32 {
   112  	return client.activeNum.Load()
   113  }
   114  
   115  // Request means two way request.
   116  func (client *ExchangeClient) Request(invocation *protocol.Invocation, url *common.URL, timeout time.Duration,
   117  	result *protocol.RPCResult) error {
   118  	if er := client.doInit(url); er != nil {
   119  		return er
   120  	}
   121  	request := NewRequest("2.0.2")
   122  	request.Data = invocation
   123  	request.Event = false
   124  	request.TwoWay = true
   125  
   126  	rsp := NewPendingResponse(request.ID)
   127  	rsp.response = NewResponse(request.ID, "2.0.2")
   128  	rsp.Reply = (*invocation).Reply()
   129  	AddPendingResponse(rsp)
   130  
   131  	err := client.client.Request(request, timeout, rsp)
   132  	// request error
   133  	if err != nil {
   134  		result.Err = err
   135  		return err
   136  	}
   137  	if resultTmp, ok := rsp.response.Result.(*protocol.RPCResult); ok {
   138  		result.Rest = resultTmp.Rest
   139  		result.Attrs = resultTmp.Attrs
   140  		result.Err = resultTmp.Err
   141  	} else {
   142  		logger.Warnf("[ExchangeClient.Request] The type of result is unexpected, we want *protocol.RPCResult, "+
   143  			"but we got %T", rsp.response.Result)
   144  	}
   145  	return nil
   146  }
   147  
   148  // AsyncRequest async two way request.
   149  func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url *common.URL, timeout time.Duration,
   150  	callback common.AsyncCallback, result *protocol.RPCResult) error {
   151  	if er := client.doInit(url); er != nil {
   152  		return er
   153  	}
   154  	request := NewRequest("2.0.2")
   155  	request.Data = invocation
   156  	request.Event = false
   157  	request.TwoWay = true
   158  
   159  	rsp := NewPendingResponse(request.ID)
   160  	rsp.response = NewResponse(request.ID, "2.0.2")
   161  	rsp.Callback = callback
   162  	rsp.Reply = (*invocation).Reply()
   163  	AddPendingResponse(rsp)
   164  
   165  	err := client.client.Request(request, timeout, rsp)
   166  	if err != nil {
   167  		result.Err = err
   168  		return err
   169  	}
   170  	result.Rest = rsp.response
   171  	return nil
   172  }
   173  
   174  // Send sends oneway request.
   175  func (client *ExchangeClient) Send(invocation *protocol.Invocation, url *common.URL, timeout time.Duration) error {
   176  	if er := client.doInit(url); er != nil {
   177  		return er
   178  	}
   179  	request := NewRequest("2.0.2")
   180  	request.Data = invocation
   181  	request.Event = false
   182  	request.TwoWay = false
   183  
   184  	rsp := NewPendingResponse(request.ID)
   185  	rsp.response = NewResponse(request.ID, "2.0.2")
   186  
   187  	err := client.client.Request(request, timeout, rsp)
   188  	if err != nil {
   189  		return err
   190  	}
   191  	return nil
   192  }
   193  
   194  // Close close the client.
   195  func (client *ExchangeClient) Close() {
   196  	client.client.Close()
   197  	client.init = false
   198  }
   199  
   200  // IsAvailable to check if the underlying network client is available yet.
   201  func (client *ExchangeClient) IsAvailable() bool {
   202  	return client.client.IsAvailable()
   203  }