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  }