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 }