github.com/matrixorigin/matrixone@v1.2.0/pkg/cnservice/cnclient/client.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cnclient
    16  
    17  import (
    18  	"fmt"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/fagongzi/goetty/v2"
    23  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    24  	"github.com/matrixorigin/matrixone/pkg/common/morpc"
    25  	"github.com/matrixorigin/matrixone/pkg/logutil"
    26  	"github.com/matrixorigin/matrixone/pkg/pb/pipeline"
    27  	"github.com/matrixorigin/matrixone/pkg/txn/rpc"
    28  )
    29  
    30  const (
    31  	//@todo need to find out why rpc timeout, or move heartbeat check to another way.
    32  	defaultRPCTimeout = 120 * time.Second
    33  )
    34  
    35  // client each node will hold only one client.
    36  // It is responsible for sending messages to other nodes. and messages were received
    37  // and handled by cn-server.
    38  var client = &CNClient{
    39  	ready:       false,
    40  	requestPool: &sync.Pool{New: func() any { return &pipeline.Message{} }},
    41  }
    42  
    43  func CloseCNClient() error {
    44  	return client.Close()
    45  }
    46  
    47  func GetStreamSender(backend string) (morpc.Stream, error) {
    48  	return client.NewStream(backend)
    49  }
    50  
    51  func AcquireMessage() *pipeline.Message {
    52  	return client.acquireMessage().(*pipeline.Message)
    53  }
    54  
    55  func IsCNClientReady() bool {
    56  	client.Lock()
    57  	defer client.Unlock()
    58  
    59  	return client.ready
    60  }
    61  
    62  type CNClient struct {
    63  	sync.Mutex
    64  
    65  	localServiceAddress string
    66  	ready               bool
    67  	config              *ClientConfig
    68  	client              morpc.RPCClient
    69  
    70  	// pool for send message
    71  	requestPool *sync.Pool
    72  }
    73  
    74  func (c *CNClient) NewStream(backend string) (morpc.Stream, error) {
    75  	c.Lock()
    76  	defer c.Unlock()
    77  	if !c.ready {
    78  		return nil, moerr.NewInternalErrorNoCtx("cn client is not ready")
    79  	}
    80  
    81  	if backend == c.localServiceAddress {
    82  		return nil, moerr.NewInternalErrorNoCtx(fmt.Sprintf("remote run pipeline in local: %s", backend))
    83  	}
    84  	return c.client.NewStream(backend, true)
    85  }
    86  
    87  func (c *CNClient) Close() error {
    88  	c.Lock()
    89  	defer c.Unlock()
    90  
    91  	c.ready = false
    92  	if c.client != nil {
    93  		return c.client.Close()
    94  	}
    95  	return nil
    96  }
    97  
    98  const (
    99  	dfMaxSenderNumber       = 100000
   100  	dfConnectTimeout        = 5 * time.Second
   101  	dfClientReadBufferSize  = 1 << 10
   102  	dfClientWriteBufferSize = 1 << 10
   103  )
   104  
   105  // ClientConfig a config to init a CNClient
   106  type ClientConfig struct {
   107  	// MaxSenderNumber is the max number of backends per host for compute node service.
   108  	MaxSenderNumber int
   109  	// TimeOutForEachConnect is the out time for each tcp connect.
   110  	TimeOutForEachConnect time.Duration
   111  	// related buffer size.
   112  	ReadBufferSize  int
   113  	WriteBufferSize int
   114  	// RPC rpc config
   115  	RPC rpc.Config
   116  }
   117  
   118  // TODO: Here it needs to be refactored together with Runtime
   119  func NewCNClient(
   120  	localServiceAddress string,
   121  	cfg *ClientConfig) error {
   122  	logger := logutil.GetGlobalLogger().Named("cn-backend")
   123  
   124  	var err error
   125  	cfg.Fill()
   126  
   127  	client.Lock()
   128  	defer client.Unlock()
   129  	if client.ready {
   130  		return nil
   131  	}
   132  
   133  	cli := client
   134  	cli.config = cfg
   135  	cli.localServiceAddress = localServiceAddress
   136  	cli.requestPool = &sync.Pool{New: func() any { return &pipeline.Message{} }}
   137  
   138  	codec := morpc.NewMessageCodec(cli.acquireMessage,
   139  		morpc.WithCodecMaxBodySize(int(cfg.RPC.MaxMessageSize)))
   140  	factory := morpc.NewGoettyBasedBackendFactory(codec,
   141  		morpc.WithBackendGoettyOptions(
   142  			goetty.WithSessionRWBUfferSize(cfg.ReadBufferSize, cfg.WriteBufferSize),
   143  			goetty.WithSessionReleaseMsgFunc(func(v any) {
   144  				m := v.(morpc.RPCMessage)
   145  				if !m.InternalMessage() {
   146  					cli.releaseMessage(m.Message.(*pipeline.Message))
   147  				}
   148  			}),
   149  		),
   150  		morpc.WithBackendReadTimeout(defaultRPCTimeout),
   151  		morpc.WithBackendConnectTimeout(cfg.TimeOutForEachConnect),
   152  		morpc.WithBackendLogger(logger),
   153  	)
   154  
   155  	cli.client, err = morpc.NewClient(
   156  		"pipeline-client",
   157  		factory,
   158  		morpc.WithClientMaxBackendPerHost(cfg.MaxSenderNumber),
   159  		morpc.WithClientLogger(logger),
   160  	)
   161  	cli.ready = err == nil
   162  	return nil
   163  }
   164  
   165  func (c *CNClient) acquireMessage() morpc.Message {
   166  	// TODO: pipeline.Message has many []byte fields, maybe can use PayloadMessage to avoid mem copy.
   167  	return c.requestPool.Get().(*pipeline.Message)
   168  }
   169  
   170  func (c *CNClient) releaseMessage(m *pipeline.Message) {
   171  	if c.requestPool != nil {
   172  		m.Reset()
   173  		c.requestPool.Put(m)
   174  	}
   175  }
   176  
   177  // Fill set some default value for client config.
   178  func (cfg *ClientConfig) Fill() {
   179  	if cfg.MaxSenderNumber <= 0 {
   180  		cfg.MaxSenderNumber = dfMaxSenderNumber
   181  	}
   182  	if cfg.ReadBufferSize < 0 {
   183  		cfg.ReadBufferSize = dfClientReadBufferSize
   184  	}
   185  	if cfg.WriteBufferSize < 0 {
   186  		cfg.WriteBufferSize = dfClientWriteBufferSize
   187  	}
   188  	if cfg.TimeOutForEachConnect <= 0 {
   189  		cfg.TimeOutForEachConnect = dfConnectTimeout
   190  	}
   191  }
   192  
   193  func GetRPCClient() morpc.RPCClient {
   194  	client.Lock()
   195  	defer client.Unlock()
   196  
   197  	if client.ready {
   198  		return client.client
   199  	}
   200  	return nil
   201  }