dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/getty/listener.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 "sync" 22 "sync/atomic" 23 "time" 24 ) 25 26 import ( 27 getty "github.com/apache/dubbo-getty" 28 29 hessian "github.com/apache/dubbo-go-hessian2" 30 31 "github.com/dubbogo/gost/log/logger" 32 gxtime "github.com/dubbogo/gost/time" 33 34 perrors "github.com/pkg/errors" 35 ) 36 37 import ( 38 "dubbo.apache.org/dubbo-go/v3/common/constant" 39 "dubbo.apache.org/dubbo-go/v3/protocol/invocation" 40 "dubbo.apache.org/dubbo-go/v3/remoting" 41 ) 42 43 const ( 44 WritePkg_Timeout = 5 * time.Second // TODO: WritePkg_Timeout will entry *.yml 45 ) 46 47 var ( 48 errTooManySessions = perrors.New("too many sessions") 49 errHeartbeatReadTimeout = perrors.New("heartbeat read timeout") 50 ) 51 52 type rpcSession struct { 53 session getty.Session 54 reqNum int32 55 } 56 57 func (s *rpcSession) AddReqNum(num int32) { 58 atomic.AddInt32(&s.reqNum, num) 59 } 60 61 func (s *rpcSession) GetReqNum() int32 { 62 return atomic.LoadInt32(&s.reqNum) 63 } 64 65 // nolint 66 type RpcClientHandler struct { 67 conn *gettyRPCClient 68 timeoutTimes int 69 } 70 71 // nolint 72 func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { 73 return &RpcClientHandler{conn: client} 74 } 75 76 // OnOpen call the getty client session opened, add the session to getty client session list 77 func (h *RpcClientHandler) OnOpen(session getty.Session) error { 78 h.conn.addSession(session) 79 return nil 80 } 81 82 // OnError the getty client session has errored, so remove the session from the getty client session list 83 func (h *RpcClientHandler) OnError(session getty.Session, err error) { 84 logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) 85 h.conn.removeSession(session) 86 } 87 88 // OnClose close the session, remove it from the getty session list 89 func (h *RpcClientHandler) OnClose(session getty.Session) { 90 logger.Infof("session{%s} is closing......", session.Stat()) 91 h.conn.removeSession(session) 92 } 93 94 // OnMessage get response from getty server, and update the session to the getty client session list 95 func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { 96 result, ok := pkg.(*remoting.DecodeResult) 97 if !ok || result == ((*remoting.DecodeResult)(nil)) { 98 logger.Errorf("[RpcClientHandler.OnMessage] getty client gets an unexpected rpc result: %#v", result) 99 return 100 } 101 // get heartbeat request from server 102 if result.IsRequest { 103 req := result.Result.(*remoting.Request) 104 if req.Event { 105 logger.Debugf("[RpcClientHandler.OnMessage] getty client gets a heartbeat request: %#v", req) 106 resp := remoting.NewResponse(req.ID, req.Version) 107 resp.Status = hessian.Response_OK 108 resp.Event = req.Event 109 resp.SerialID = req.SerialID 110 resp.Version = "2.0.2" 111 reply(session, resp) 112 return 113 } 114 logger.Errorf("[RpcClientHandler.OnMessage] unexpected heartbeat request: %#v", req) 115 return 116 } 117 h.timeoutTimes = 0 118 p := result.Result.(*remoting.Response) 119 // get heartbeat 120 if p.Event { 121 logger.Debugf("[RpcClientHandler.OnMessage] getty client received a heartbeat response: %s", p) 122 if p.Error != nil { 123 logger.Errorf("[RpcClientHandler.OnMessage] a heartbeat response received by the getty client "+ 124 "encounters an error: %v", p.Error) 125 } 126 p.Handle() 127 return 128 } 129 130 logger.Debugf("[RpcClientHandler.OnMessage] getty client received a response: %s", p) 131 132 h.conn.updateSession(session) 133 134 p.Handle() 135 } 136 137 // OnCron check the session health periodic. if the session's sessionTimeout has reached, just close the session 138 func (h *RpcClientHandler) OnCron(session getty.Session) { 139 rs, err := h.conn.getClientRpcSession(session) 140 if err != nil { 141 logger.Errorf("client.getClientSession(session{%s}) = error{%v}", 142 session.Stat(), perrors.WithStack(err)) 143 return 144 } 145 if h.conn.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { 146 logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", 147 session.Stat(), time.Since(session.GetActive()).String(), rs.reqNum) 148 h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) 149 return 150 } 151 152 heartbeatCallBack := func(err error) { 153 if err != nil { 154 logger.Warnf("failed to send heartbeat, error{%v}", err) 155 if h.timeoutTimes >= 3 { 156 h.conn.removeSession(session) 157 return 158 } 159 h.timeoutTimes++ 160 return 161 } 162 h.timeoutTimes = 0 163 } 164 165 if err := heartbeat(session, h.conn.rpcClient.conf.heartbeatTimeout, heartbeatCallBack); err != nil { 166 logger.Warnf("failed to send heartbeat, error{%v}", err) 167 } 168 } 169 170 // nolint 171 type RpcServerHandler struct { 172 maxSessionNum int 173 sessionTimeout time.Duration 174 sessionMap map[getty.Session]*rpcSession 175 rwlock sync.RWMutex 176 server *Server 177 timeoutTimes int 178 } 179 180 // nolint 181 func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration, serverP *Server) *RpcServerHandler { 182 return &RpcServerHandler{ 183 maxSessionNum: maxSessionNum, 184 sessionTimeout: sessionTimeout, 185 sessionMap: make(map[getty.Session]*rpcSession), 186 server: serverP, 187 } 188 } 189 190 // OnOpen call server session opened, add the session to getty server session list. also onOpen 191 // will check the max getty server session number 192 func (h *RpcServerHandler) OnOpen(session getty.Session) error { 193 var err error 194 h.rwlock.RLock() 195 if h.maxSessionNum <= len(h.sessionMap) { 196 err = errTooManySessions 197 } 198 h.rwlock.RUnlock() 199 if err != nil { 200 return perrors.WithStack(err) 201 } 202 203 logger.Infof("got session:%s", session.Stat()) 204 h.rwlock.Lock() 205 h.sessionMap[session] = &rpcSession{session: session} 206 h.rwlock.Unlock() 207 return nil 208 } 209 210 // OnError the getty server session has errored, so remove the session from the getty server session list 211 func (h *RpcServerHandler) OnError(session getty.Session, err error) { 212 logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) 213 h.rwlock.Lock() 214 delete(h.sessionMap, session) 215 h.rwlock.Unlock() 216 } 217 218 // OnClose close the session, remove it from the getty server list 219 func (h *RpcServerHandler) OnClose(session getty.Session) { 220 logger.Infof("session{%s} is closing......", session.Stat()) 221 h.rwlock.Lock() 222 delete(h.sessionMap, session) 223 h.rwlock.Unlock() 224 } 225 226 // OnMessage get request from getty client, update the session reqNum and reply response to client 227 func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { 228 h.rwlock.Lock() 229 if _, ok := h.sessionMap[session]; ok { 230 h.sessionMap[session].reqNum++ 231 } 232 h.rwlock.Unlock() 233 234 decodeResult, drOK := pkg.(*remoting.DecodeResult) 235 if !drOK || decodeResult == ((*remoting.DecodeResult)(nil)) { 236 logger.Errorf("illegal package{%#v}", pkg) 237 return 238 } 239 if !decodeResult.IsRequest { 240 res := decodeResult.Result.(*remoting.Response) 241 if res.Event { 242 logger.Debugf("get rpc heartbeat response{%#v}", res) 243 if res.Error != nil { 244 logger.Errorf("rpc heartbeat response{error: %#v}", res.Error) 245 } 246 res.Handle() 247 return 248 } 249 logger.Errorf("illegal package but not heartbeat. {%#v}", pkg) 250 return 251 } 252 req := decodeResult.Result.(*remoting.Request) 253 254 resp := remoting.NewResponse(req.ID, req.Version) 255 resp.Status = hessian.Response_OK 256 resp.Event = req.Event 257 resp.SerialID = req.SerialID 258 resp.Version = "2.0.2" 259 260 // heartbeat 261 if req.Event { 262 logger.Debugf("get rpc heartbeat request{%#v}", resp) 263 reply(session, resp) 264 return 265 } 266 267 invoc, ok := req.Data.(*invocation.RPCInvocation) 268 if !ok { 269 panic("create invocation occur some exception for the type is not suitable one.") 270 } 271 attachments := invoc.Attachments() 272 attachments[constant.LocalAddr] = session.LocalAddr() 273 attachments[constant.RemoteAddr] = session.RemoteAddr() 274 275 result := h.server.requestHandler(invoc) 276 if !req.TwoWay { 277 return 278 } 279 resp.Result = result 280 281 reply(session, resp) 282 } 283 284 // OnCron check the session health periodic. if the session's sessionTimeout has reached, just close the session 285 func (h *RpcServerHandler) OnCron(session getty.Session) { 286 var ( 287 flag bool 288 active time.Time 289 ) 290 291 h.rwlock.RLock() 292 if _, ok := h.sessionMap[session]; ok { 293 active = session.GetActive() 294 if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() { 295 flag = true 296 logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", 297 session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum) 298 } 299 } 300 h.rwlock.RUnlock() 301 302 if flag { 303 h.rwlock.Lock() 304 delete(h.sessionMap, session) 305 h.rwlock.Unlock() 306 session.Close() 307 } 308 309 heartbeatCallBack := func(err error) { 310 if err != nil { 311 logger.Warnf("failed to send heartbeat, error{%v}", err) 312 if h.timeoutTimes >= 3 { 313 h.rwlock.Lock() 314 delete(h.sessionMap, session) 315 h.rwlock.Unlock() 316 session.Close() 317 return 318 } 319 h.timeoutTimes++ 320 return 321 } 322 h.timeoutTimes = 0 323 } 324 325 if err := heartbeat(session, h.server.conf.heartbeatTimeout, heartbeatCallBack); err != nil { 326 logger.Warnf("failed to send heartbeat, error{%v}", err) 327 } 328 } 329 330 func reply(session getty.Session, resp *remoting.Response) { 331 if totalLen, sendLen, err := session.WritePkg(resp, WritePkg_Timeout); err != nil { 332 if sendLen != 0 && totalLen != sendLen { 333 logger.Warnf("start to close the session at replying because %d of %d bytes data is sent success. err:%+v", sendLen, totalLen, err) 334 go session.Close() 335 } 336 logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), resp) 337 } 338 } 339 340 func heartbeat(session getty.Session, timeout time.Duration, callBack func(err error)) error { 341 req := remoting.NewRequest("2.0.2") 342 req.TwoWay = true 343 req.Event = true 344 resp := remoting.NewPendingResponse(req.ID) 345 remoting.AddPendingResponse(resp) 346 totalLen, sendLen, err := session.WritePkg(req, -1) 347 if sendLen != 0 && totalLen != sendLen { 348 logger.Warnf("start to close the session at heartbeat because %d of %d bytes data is sent success. err:%+v", sendLen, totalLen, err) 349 go session.Close() 350 } 351 352 go func() { 353 var err1 error 354 select { 355 case <-gxtime.After(timeout): 356 err1 = errHeartbeatReadTimeout 357 case <-resp.Done: 358 err1 = resp.Err 359 } 360 callBack(err1) 361 }() 362 363 return perrors.WithStack(err) 364 }