dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/getty/pool.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  	"crypto/tls"
    22  	"fmt"
    23  	"math/rand"
    24  	"net"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  )
    29  
    30  import (
    31  	getty "github.com/apache/dubbo-getty"
    32  
    33  	"github.com/dubbogo/gost/log/logger"
    34  
    35  	perrors "github.com/pkg/errors"
    36  )
    37  
    38  type gettyRPCClient struct {
    39  	once   sync.Once
    40  	addr   string // protocol string
    41  	active int64  // zero, not create or be destroyed
    42  
    43  	rpcClient *Client
    44  
    45  	lock        sync.RWMutex
    46  	gettyClient getty.Client
    47  	sessions    []*rpcSession
    48  }
    49  
    50  func newGettyRPCClientConn(rpcClient *Client, addr string) (*gettyRPCClient, error) {
    51  	var (
    52  		gettyClient getty.Client
    53  		sslEnabled  bool
    54  	)
    55  	sslEnabled = rpcClient.conf.SSLEnabled
    56  	clientOpts := []getty.ClientOption{
    57  		getty.WithServerAddress(addr),
    58  		getty.WithConnectionNumber((int)(rpcClient.conf.ConnectionNum)),
    59  		getty.WithReconnectInterval(rpcClient.conf.ReconnectInterval),
    60  	}
    61  	if sslEnabled {
    62  		logger.Infof("Getty client initialized the TLS configuration")
    63  		clientOpts = append(clientOpts, getty.WithClientSslEnabled(sslEnabled), getty.WithClientTlsConfigBuilder(rpcClient.conf.TLSBuilder))
    64  	}
    65  
    66  	if clientGrPool != nil {
    67  		clientOpts = append(clientOpts, getty.WithClientTaskPool(clientGrPool))
    68  	}
    69  
    70  	gettyClient = getty.NewTCPClient(clientOpts...)
    71  	c := &gettyRPCClient{
    72  		addr:        addr,
    73  		rpcClient:   rpcClient,
    74  		gettyClient: gettyClient,
    75  	}
    76  	go c.gettyClient.RunEventLoop(c.newSession)
    77  
    78  	idx := 1
    79  	start := time.Now()
    80  	connectTimeout := rpcClient.opts.ConnectTimeout
    81  	for {
    82  		idx++
    83  		if c.isAvailable() {
    84  			break
    85  		}
    86  
    87  		if time.Since(start) > connectTimeout {
    88  			c.gettyClient.Close()
    89  			return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %s", addr, connectTimeout))
    90  		}
    91  
    92  		interval := time.Millisecond * time.Duration(idx)
    93  		if interval > time.Duration(100e6) {
    94  			interval = 100e6 // 100 ms
    95  		}
    96  		time.Sleep(interval)
    97  	}
    98  	logger.Debug("client init ok")
    99  	c.updateActive(time.Now().Unix())
   100  
   101  	return c, nil
   102  }
   103  
   104  func (c *gettyRPCClient) updateActive(active int64) {
   105  	atomic.StoreInt64(&c.active, active)
   106  }
   107  
   108  func (c *gettyRPCClient) getActive() int64 {
   109  	return atomic.LoadInt64(&c.active)
   110  }
   111  
   112  func (c *gettyRPCClient) newSession(session getty.Session) error {
   113  	var (
   114  		ok         bool
   115  		tcpConn    *net.TCPConn
   116  		conf       ClientConfig
   117  		sslEnabled bool
   118  	)
   119  	conf = c.rpcClient.conf
   120  	sslEnabled = c.rpcClient.sslEnabled
   121  	if conf.GettySessionParam.CompressEncoding {
   122  		session.SetCompressType(getty.CompressZip)
   123  	}
   124  	if sslEnabled {
   125  		if _, ok = session.Conn().(*tls.Conn); !ok {
   126  			panic(fmt.Sprintf("%s, session.conn{%#v} is not tls connection\n", session.Stat(), session.Conn()))
   127  		}
   128  		session.SetName(conf.GettySessionParam.SessionName)
   129  		session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
   130  		session.SetPkgHandler(NewRpcClientPackageHandler(c.rpcClient))
   131  		session.SetEventListener(NewRpcClientHandler(c))
   132  		session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
   133  		session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
   134  		session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6))
   135  		session.SetWaitTime(conf.GettySessionParam.waitTimeout)
   136  		logger.Debugf("client new session:%s\n", session.Stat())
   137  		return nil
   138  	}
   139  	if tcpConn, ok = session.Conn().(*net.TCPConn); !ok {
   140  		panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn()))
   141  	}
   142  
   143  	if err := tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay); err != nil {
   144  		logger.Error("tcpConn.SetNoDelay() = error:%v", err)
   145  	}
   146  	if err := tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive); err != nil {
   147  		logger.Error("tcpConn.SetKeepAlive() = error:%v", err)
   148  	}
   149  	if conf.GettySessionParam.TcpKeepAlive {
   150  		if err := tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod); err != nil {
   151  			logger.Error("tcpConn.SetKeepAlivePeriod() = error:%v", err)
   152  		}
   153  	}
   154  	if err := tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize); err != nil {
   155  		logger.Error("tcpConn.SetReadBuffer() = error:%v", err)
   156  	}
   157  	if err := tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize); err != nil {
   158  		logger.Error("tcpConn.SetWriteBuffer() = error:%v", err)
   159  	}
   160  
   161  	session.SetName(conf.GettySessionParam.SessionName)
   162  	session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
   163  	session.SetPkgHandler(NewRpcClientPackageHandler(c.rpcClient))
   164  	session.SetEventListener(NewRpcClientHandler(c))
   165  	session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
   166  	session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
   167  	session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6))
   168  	session.SetWaitTime(conf.GettySessionParam.waitTimeout)
   169  	logger.Debugf("client new session:%s\n", session.Stat())
   170  	return nil
   171  }
   172  
   173  func (c *gettyRPCClient) selectSession() getty.Session {
   174  	c.lock.RLock()
   175  	defer c.lock.RUnlock()
   176  
   177  	if c.sessions == nil {
   178  		return nil
   179  	}
   180  	count := len(c.sessions)
   181  	if count == 0 {
   182  		return nil
   183  	}
   184  	return c.sessions[rand.Int31n(int32(count))].session
   185  }
   186  
   187  func (c *gettyRPCClient) addSession(session getty.Session) {
   188  	logger.Debugf("add session{%s}", session.Stat())
   189  	if session == nil {
   190  		return
   191  	}
   192  
   193  	c.lock.Lock()
   194  	defer c.lock.Unlock()
   195  	if c.sessions == nil {
   196  		c.sessions = make([]*rpcSession, 0, 16)
   197  	}
   198  	c.sessions = append(c.sessions, &rpcSession{session: session})
   199  }
   200  
   201  func (c *gettyRPCClient) removeSession(session getty.Session) {
   202  	if session == nil {
   203  		return
   204  	}
   205  
   206  	var removeFlag bool
   207  	func() {
   208  		c.lock.Lock()
   209  		defer c.lock.Unlock()
   210  		if c.sessions == nil {
   211  			return
   212  		}
   213  
   214  		for i, s := range c.sessions {
   215  			if s.session == session {
   216  				c.sessions = append(c.sessions[:i], c.sessions[i+1:]...)
   217  				logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i)
   218  				break
   219  			}
   220  		}
   221  		logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions))
   222  		if len(c.sessions) == 0 {
   223  			removeFlag = true
   224  		}
   225  	}()
   226  	if removeFlag {
   227  		c.rpcClient.resetRpcConn()
   228  		c.close()
   229  	}
   230  }
   231  
   232  func (c *gettyRPCClient) updateSession(session getty.Session) {
   233  	if session == nil {
   234  		return
   235  	}
   236  
   237  	var rs *rpcSession
   238  	func() {
   239  		c.lock.RLock()
   240  		defer c.lock.RUnlock()
   241  		if c.sessions == nil {
   242  			return
   243  		}
   244  
   245  		for i, s := range c.sessions {
   246  			if s.session == session {
   247  				rs = c.sessions[i]
   248  				break
   249  			}
   250  		}
   251  	}()
   252  	if rs != nil {
   253  		rs.AddReqNum(1)
   254  	}
   255  }
   256  
   257  func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) {
   258  	var (
   259  		err error
   260  		rs  rpcSession
   261  	)
   262  	c.lock.RLock()
   263  	defer c.lock.RUnlock()
   264  	if c.sessions == nil {
   265  		return rs, errClientClosed
   266  	}
   267  
   268  	err = errSessionNotExist
   269  	for _, s := range c.sessions {
   270  		if s.session == session {
   271  			rs = *s
   272  			err = nil
   273  			break
   274  		}
   275  	}
   276  
   277  	return rs, perrors.WithStack(err)
   278  }
   279  
   280  func (c *gettyRPCClient) isAvailable() bool {
   281  	return c.selectSession() != nil
   282  }
   283  
   284  func (c *gettyRPCClient) close() error {
   285  	closeErr := perrors.Errorf("close gettyRPCClient{%#v} again", c)
   286  	c.once.Do(func() {
   287  		var (
   288  			gettyClient getty.Client
   289  			sessions    []*rpcSession
   290  		)
   291  		func() {
   292  			c.lock.Lock()
   293  			defer c.lock.Unlock()
   294  
   295  			gettyClient = c.gettyClient
   296  			c.gettyClient = nil
   297  
   298  			sessions = make([]*rpcSession, 0, len(c.sessions))
   299  			sessions = append(sessions, c.sessions...)
   300  			c.sessions = c.sessions[:0]
   301  		}()
   302  
   303  		c.updateActive(0)
   304  
   305  		go func() {
   306  			if gettyClient != nil {
   307  				gettyClient.Close()
   308  			}
   309  			for _, s := range sessions {
   310  				logger.Infof("close client session{%s, last active:%s, request number:%d}",
   311  					s.session.Stat(), s.session.GetActive().String(), s.GetReqNum())
   312  				s.session.Close()
   313  			}
   314  		}()
   315  
   316  		closeErr = nil
   317  	})
   318  	return closeErr
   319  }