Skip to content

Commit f5a2964

Browse files
committed
feat: add client connection hang
1 parent f54394f commit f5a2964

File tree

1 file changed

+150
-83
lines changed

1 file changed

+150
-83
lines changed

server/connector.go

Lines changed: 150 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ import (
2525

2626
const (
2727
handshakeTimeout = 5 * time.Second
28-
backendTimeout = 1 * time.Second
28+
backendTimeout = 30 * time.Second
29+
backendRetryInterval = 5 * time.Second
30+
backendStatusTimeout = 1 * time.Second
2931
)
3032

3133
var noDeadline time.Time
@@ -355,92 +357,157 @@ func (c *Connector) readPlayerInfo(bufferedReader *bufio.Reader, clientAddr net.
355357
}
356358

357359
func (c *Connector) findAndConnectBackend(ctx context.Context, frontendConn net.Conn,
358-
clientAddr net.Addr, preReadContent io.Reader, serverAddress string, playerInfo *PlayerInfo, nextState mcproto.State) {
359-
360-
backendHostPort, resolvedHost, waker := Routes.FindBackendForServerAddress(ctx, serverAddress)
361-
if waker != nil && nextState > mcproto.StateStatus {
362-
serverAllowsPlayer := c.autoScaleUpAllowDenyConfig.ServerAllowsPlayer(serverAddress, playerInfo)
363-
logrus.
364-
WithField("client", clientAddr).
365-
WithField("server", serverAddress).
366-
WithField("player", playerInfo).
367-
WithField("serverAllowsPlayer", serverAllowsPlayer).
368-
Debug("checked if player is allowed to wake up the server")
369-
if serverAllowsPlayer {
370-
if err := waker(ctx); err != nil {
371-
logrus.WithFields(logrus.Fields{"serverAddress": serverAddress}).WithError(err).Error("failed to wake up backend")
372-
c.metrics.Errors.With("type", "wakeup_failed").Add(1)
373-
return
374-
}
375-
}
376-
}
377-
378-
if backendHostPort == "" {
379-
logrus.
380-
WithField("serverAddress", serverAddress).
381-
WithField("resolvedHost", resolvedHost).
382-
WithField("player", playerInfo).
383-
Warn("Unable to find registered backend")
384-
c.metrics.Errors.With("type", "missing_backend").Add(1)
385-
386-
if c.connectionNotifier != nil {
387-
err := c.connectionNotifier.NotifyMissingBackend(ctx, clientAddr, serverAddress, playerInfo)
388-
if err != nil {
389-
logrus.WithError(err).Warn("failed to notify missing backend")
390-
}
391-
}
392-
393-
return
394-
}
395-
396-
logrus.
397-
WithField("client", clientAddr).
398-
WithField("server", serverAddress).
399-
WithField("backendHostPort", backendHostPort).
400-
WithField("player", playerInfo).
401-
Info("Connecting to backend")
402-
403-
backendConn, err := net.DialTimeout("tcp", backendHostPort, backendTimeout)
404-
if err != nil {
360+
clientAddr net.Addr, preReadContent io.Reader, serverAddress string, playerInfo *PlayerInfo, nextState mcproto.State) {
361+
362+
backendHostPort, resolvedHost, waker := Routes.FindBackendForServerAddress(ctx, serverAddress)
363+
if waker != nil && nextState > mcproto.StateStatus {
364+
serverAllowsPlayer := c.autoScaleUpAllowDenyConfig.ServerAllowsPlayer(serverAddress, playerInfo)
365+
logrus.
366+
WithField("client", clientAddr).
367+
WithField("server", serverAddress).
368+
WithField("player", playerInfo).
369+
WithField("serverAllowsPlayer", serverAllowsPlayer).
370+
Debug("checked if player is allowed to wake up the server")
371+
if serverAllowsPlayer {
372+
if err := waker(ctx); err != nil {
373+
logrus.WithFields(logrus.Fields{"serverAddress": serverAddress}).WithError(err).Error("failed to wake up backend")
374+
c.metrics.Errors.With("type", "wakeup_failed").Add(1)
375+
return
376+
}
377+
}
378+
}
379+
380+
if backendHostPort == "" {
381+
logrus.
382+
WithField("serverAddress", serverAddress).
383+
WithField("resolvedHost", resolvedHost).
384+
WithField("player", playerInfo).
385+
Warn("Unable to find registered backend")
386+
c.metrics.Errors.With("type", "missing_backend").Add(1)
387+
388+
if c.connectionNotifier != nil {
389+
err := c.connectionNotifier.NotifyMissingBackend(ctx, clientAddr, serverAddress, playerInfo)
390+
if err != nil {
391+
logrus.WithError(err).Warn("failed to notify missing backend")
392+
}
393+
}
394+
395+
return
396+
}
397+
398+
logrus.
399+
WithField("client", clientAddr).
400+
WithField("server", serverAddress).
401+
WithField("backendHostPort", backendHostPort).
402+
WithField("player", playerInfo).
403+
Info("Connecting to backend")
404+
405+
var backendConn net.Conn
406+
var err error
407+
408+
if nextState == mcproto.StateStatus {
409+
// Status request: try to connect once with backendStatusTimeout
410+
backendConn, err = net.DialTimeout("tcp", backendHostPort, backendStatusTimeout)
411+
if err != nil {
412+
logrus.
413+
WithError(err).
414+
WithField("client", clientAddr).
415+
WithField("serverAddress", serverAddress).
416+
WithField("backend", backendHostPort).
417+
WithField("player", playerInfo).
418+
Warn("Unable to connect to backend for status request")
419+
c.metrics.Errors.With("type", "backend_failed").Add(1)
420+
421+
if c.connectionNotifier != nil {
422+
notifyErr := c.connectionNotifier.NotifyFailedBackendConnection(ctx, clientAddr, serverAddress, playerInfo, backendHostPort, err)
423+
if notifyErr != nil {
424+
logrus.WithError(notifyErr).Warn("failed to notify failed backend connection")
425+
}
426+
}
427+
428+
// Return fakeOnline MOTD
429+
if c.fakeOnline && waker != nil {
430+
logrus.Info("Server is offline, sending fakeOnlineMOTD for status request")
431+
writeStatusErr := mcproto.WriteStatusResponse(
432+
frontendConn,
433+
c.fakeOnlineMOTD,
434+
)
435+
if writeStatusErr != nil {
436+
logrus.
437+
WithError(writeStatusErr).
438+
WithField("client", clientAddr).
439+
WithField("serverAddress", serverAddress).
440+
WithField("backend", backendHostPort).
441+
WithField("player", playerInfo).
442+
Error("Failed to write status response")
443+
}
444+
}
445+
return
446+
}
447+
} else if nextState == mcproto.StateLogin {
448+
// Connect request
449+
if waker != nil {
450+
logrus.Debug("Connect: Autoscaler is enabled, waiting for backend to be ready")
451+
// Autoscaler enabled: retry until backendTimeout is reached
452+
deadline := time.Now().Add(backendTimeout)
453+
for {
454+
backendConn, err = net.DialTimeout("tcp", backendHostPort, backendRetryInterval)
455+
logrus.Debug("Tries to connect to backend")
456+
457+
if err == nil {
458+
break
459+
}
460+
if time.Now().After(deadline) {
461+
logrus.
462+
WithError(err).
463+
WithField("client", clientAddr).
464+
WithField("serverAddress", serverAddress).
465+
WithField("backend", backendHostPort).
466+
WithField("player", playerInfo).
467+
Warn("Unable to connect to backend after retries (autoscaler enabled)")
468+
c.metrics.Errors.With("type", "backend_failed").Add(1)
469+
if c.connectionNotifier != nil {
470+
notifyErr := c.connectionNotifier.NotifyFailedBackendConnection(ctx, clientAddr, serverAddress, playerInfo, backendHostPort, err)
471+
if notifyErr != nil {
472+
logrus.WithError(notifyErr).Warn("failed to notify failed backend connection")
473+
}
474+
}
475+
return
476+
}
477+
time.Sleep(backendRetryInterval)
478+
}
479+
} else {
480+
logrus.Debug("Connect: Autoscaler is disabled, trying to connect once")
481+
// Autoscaler disabled: try to connect once with backendTimeout
482+
backendConn, err = net.DialTimeout("tcp", backendHostPort, backendTimeout)
483+
if err != nil {
484+
logrus.
485+
WithError(err).
486+
WithField("client", clientAddr).
487+
WithField("serverAddress", serverAddress).
488+
WithField("backend", backendHostPort).
489+
WithField("player", playerInfo).
490+
Warn("Unable to connect to backend (autoscaler disabled)")
491+
c.metrics.Errors.With("type", "backend_failed").Add(1)
492+
if c.connectionNotifier != nil {
493+
notifyErr := c.connectionNotifier.NotifyFailedBackendConnection(ctx, clientAddr, serverAddress, playerInfo, backendHostPort, err)
494+
if notifyErr != nil {
495+
logrus.WithError(notifyErr).Warn("failed to notify failed backend connection")
496+
}
497+
}
498+
return
499+
}
500+
}
501+
} else {
502+
// Unknown state, do nothing
405503
logrus.
406-
WithError(err).
407504
WithField("client", clientAddr).
408505
WithField("serverAddress", serverAddress).
409-
WithField("backend", backendHostPort).
506+
WithField("nextState", nextState).
410507
WithField("player", playerInfo).
411-
Warn("Unable to connect to backend")
412-
c.metrics.Errors.With("type", "backend_failed").Add(1)
413-
414-
if c.connectionNotifier != nil {
415-
notifyErr := c.connectionNotifier.NotifyFailedBackendConnection(ctx, clientAddr, serverAddress, playerInfo, backendHostPort, err)
416-
if notifyErr != nil {
417-
logrus.WithError(notifyErr).Warn("failed to notify failed backend connection")
418-
}
419-
}
420-
421-
// Verify that the packet is a status request && autoScaleUp is enabled
422-
if c.fakeOnline && waker != nil && nextState == mcproto.StateStatus {
423-
logrus.Info("Server is offline, sending fakeOnlineMOTD")
424-
425-
// Send a response to the client indicating that the server is sleeping
426-
writeStatusErr := mcproto.WriteStatusResponse(
427-
frontendConn,
428-
c.fakeOnlineMOTD,
429-
)
430-
431-
if writeStatusErr != nil {
432-
logrus.
433-
WithError(writeStatusErr).
434-
WithField("client", clientAddr).
435-
WithField("serverAddress", serverAddress).
436-
WithField("backend", backendHostPort).
437-
WithField("player", playerInfo).
438-
Error("Failed to write status response")
439-
}
440-
}
441-
442-
return
443-
}
508+
Warn("Unknown state, unable to connect to backend")
509+
return
510+
}
444511

445512
if c.connectionNotifier != nil {
446513
err := c.connectionNotifier.NotifyConnected(ctx, clientAddr, serverAddress, playerInfo, backendHostPort)

0 commit comments

Comments
 (0)