github.com/cloudwego/kitex@v0.9.0/pkg/remote/trans/netpoll/trans_server.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Package netpoll contains server and client implementation for netpoll.
    18  
    19  package netpoll
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"net"
    25  	"runtime/debug"
    26  	"sync"
    27  	"syscall"
    28  
    29  	"github.com/cloudwego/netpoll"
    30  
    31  	"github.com/cloudwego/kitex/pkg/klog"
    32  	"github.com/cloudwego/kitex/pkg/remote"
    33  	"github.com/cloudwego/kitex/pkg/remote/trans"
    34  	"github.com/cloudwego/kitex/pkg/utils"
    35  )
    36  
    37  // NewTransServerFactory creates a default netpoll transport server factory.
    38  func NewTransServerFactory() remote.TransServerFactory {
    39  	return &netpollTransServerFactory{}
    40  }
    41  
    42  type netpollTransServerFactory struct{}
    43  
    44  // NewTransServer implements the remote.TransServerFactory interface.
    45  func (f *netpollTransServerFactory) NewTransServer(opt *remote.ServerOption, transHdlr remote.ServerTransHandler) remote.TransServer {
    46  	return &transServer{
    47  		opt:       opt,
    48  		transHdlr: transHdlr,
    49  		lncfg:     trans.NewListenConfig(opt),
    50  	}
    51  }
    52  
    53  type transServer struct {
    54  	opt       *remote.ServerOption
    55  	transHdlr remote.ServerTransHandler
    56  
    57  	evl       netpoll.EventLoop
    58  	ln        net.Listener
    59  	lncfg     net.ListenConfig
    60  	connCount utils.AtomicInt
    61  	sync.Mutex
    62  }
    63  
    64  var _ remote.TransServer = &transServer{}
    65  
    66  // CreateListener implements the remote.TransServer interface.
    67  func (ts *transServer) CreateListener(addr net.Addr) (net.Listener, error) {
    68  	if addr.Network() == "unix" {
    69  		syscall.Unlink(addr.String())
    70  	}
    71  	// The network must be "tcp", "tcp4", "tcp6" or "unix".
    72  	ln, err := ts.lncfg.Listen(context.Background(), addr.Network(), addr.String())
    73  	return ln, err
    74  }
    75  
    76  // BootstrapServer implements the remote.TransServer interface.
    77  func (ts *transServer) BootstrapServer(ln net.Listener) (err error) {
    78  	if ln == nil {
    79  		return errors.New("listener is nil in netpoll transport server")
    80  	}
    81  	ts.ln = ln
    82  	opts := []netpoll.Option{
    83  		netpoll.WithIdleTimeout(ts.opt.MaxConnectionIdleTime),
    84  		netpoll.WithReadTimeout(ts.opt.ReadWriteTimeout),
    85  	}
    86  
    87  	ts.Lock()
    88  	opts = append(opts, netpoll.WithOnPrepare(ts.onConnActive))
    89  	ts.evl, err = netpoll.NewEventLoop(ts.onConnRead, opts...)
    90  	ts.Unlock()
    91  
    92  	if err == nil {
    93  		// Convert the listener so that closing it also stops the
    94  		// event loop in netpoll.
    95  		ts.Lock()
    96  		ts.ln, err = netpoll.ConvertListener(ts.ln)
    97  		ts.Unlock()
    98  	}
    99  	if err != nil {
   100  		return err
   101  	}
   102  	return ts.evl.Serve(ts.ln)
   103  }
   104  
   105  // Shutdown implements the remote.TransServer interface.
   106  func (ts *transServer) Shutdown() (err error) {
   107  	ts.Lock()
   108  	defer ts.Unlock()
   109  
   110  	ctx, cancel := context.WithTimeout(context.Background(), ts.opt.ExitWaitTime)
   111  	defer cancel()
   112  	if g, ok := ts.transHdlr.(remote.GracefulShutdown); ok {
   113  		if ts.ln != nil {
   114  			// 1. stop listener
   115  			ts.ln.Close()
   116  
   117  			// 2. signal all active connections to close gracefully
   118  			err = g.GracefulShutdown(ctx)
   119  			if err != nil {
   120  				klog.Warnf("KITEX: server graceful shutdown error: %v", err)
   121  			}
   122  		}
   123  	}
   124  	if ts.evl != nil {
   125  		err = ts.evl.Shutdown(ctx)
   126  	}
   127  	return
   128  }
   129  
   130  // ConnCount implements the remote.TransServer interface.
   131  func (ts *transServer) ConnCount() utils.AtomicInt {
   132  	return ts.connCount
   133  }
   134  
   135  // TODO: Is it necessary to init RPCInfo when connection is first created?
   136  // Now the strategy is the same as the original one.
   137  // 1. There's no request when the connection is first created.
   138  // 2. Doesn't need to init RPCInfo if it's not RPC request, such as heartbeat.
   139  func (ts *transServer) onConnActive(conn netpoll.Connection) context.Context {
   140  	ctx := context.Background()
   141  	defer transRecover(ctx, conn, "OnActive")
   142  	conn.AddCloseCallback(func(connection netpoll.Connection) error {
   143  		ts.onConnInactive(ctx, conn)
   144  		return nil
   145  	})
   146  	ts.connCount.Inc()
   147  	ctx, err := ts.transHdlr.OnActive(ctx, conn)
   148  	if err != nil {
   149  		ts.onError(ctx, err, conn)
   150  		conn.Close()
   151  		return ctx
   152  	}
   153  	return ctx
   154  }
   155  
   156  func (ts *transServer) onConnRead(ctx context.Context, conn netpoll.Connection) error {
   157  	err := ts.transHdlr.OnRead(ctx, conn)
   158  	if err != nil {
   159  		ts.onError(ctx, err, conn)
   160  		if conn != nil {
   161  			// close the connection if OnRead return error
   162  			conn.Close()
   163  		}
   164  	}
   165  	return nil
   166  }
   167  
   168  func (ts *transServer) onConnInactive(ctx context.Context, conn netpoll.Connection) {
   169  	defer transRecover(ctx, conn, "OnInactive")
   170  	ts.connCount.Dec()
   171  	ts.transHdlr.OnInactive(ctx, conn)
   172  }
   173  
   174  func (ts *transServer) onError(ctx context.Context, err error, conn netpoll.Connection) {
   175  	ts.transHdlr.OnError(ctx, err, conn)
   176  }
   177  
   178  func transRecover(ctx context.Context, conn netpoll.Connection, funcName string) {
   179  	panicErr := recover()
   180  	if panicErr != nil {
   181  		if conn != nil {
   182  			klog.CtxErrorf(ctx, "KITEX: panic happened in %s, remoteAddress=%s, error=%v\nstack=%s", funcName, conn.RemoteAddr(), panicErr, string(debug.Stack()))
   183  		} else {
   184  			klog.CtxErrorf(ctx, "KITEX: panic happened in %s, error=%v\nstack=%s", funcName, panicErr, string(debug.Stack()))
   185  		}
   186  	}
   187  }