mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-04-28 19:26:53 -05:00
Main: Change backend reload system
This commit is contained in:
parent
c6927a3cb6
commit
b952c9e386
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -19,6 +19,7 @@ state
|
||||||
|
|
||||||
# Executables
|
# Executables
|
||||||
*.exe
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
|
||||||
# Editor files
|
# Editor files
|
||||||
.vscode
|
.vscode
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ func ConnectFrontend() {
|
||||||
var err error
|
var err error
|
||||||
rpcFrontend, err = rpc.Dial("tcp", "localhost:29998")
|
rpcFrontend, err = rpc.Dial("tcp", "localhost:29998")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.Error("BACKEND", "Failed to connect to frontend:", err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,3 +47,29 @@ func CloseConnection(server string, index uint64) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ready will notify the frontend that the backend is ready to accept connections
|
||||||
|
func Ready() error {
|
||||||
|
if rpcFrontend == nil {
|
||||||
|
ConnectFrontend()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rpcFrontend.Call("RPCFrontendPacket.Ready", struct{}{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
logging.Error("COMMON", "Failed to notify frontend that backend is ready:", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown will notify the frontend that the backend is shutting down
|
||||||
|
func Shutdown() error {
|
||||||
|
if rpcFrontend == nil {
|
||||||
|
ConnectFrontend()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rpcFrontend.Call("RPCFrontendPacket.ShutdownBackend", struct{}{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
logging.Error("COMMON", "Failed to notify frontend that backend is shutting down:", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
112
main.go
112
main.go
|
|
@ -2,14 +2,15 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
"wwfc/api"
|
"wwfc/api"
|
||||||
"wwfc/common"
|
"wwfc/common"
|
||||||
"wwfc/gamestats"
|
"wwfc/gamestats"
|
||||||
|
|
@ -25,7 +26,9 @@ import (
|
||||||
"github.com/logrusorgru/aurora/v3"
|
"github.com/logrusorgru/aurora/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var config = common.GetConfig()
|
var (
|
||||||
|
config = common.GetConfig()
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logging.SetLevel(*config.LogLevel)
|
logging.SetLevel(*config.LogLevel)
|
||||||
|
|
@ -38,10 +41,8 @@ func main() {
|
||||||
// Start the backend instead of the frontend if the first argument is "backend"
|
// Start the backend instead of the frontend if the first argument is "backend"
|
||||||
if len(args) > 0 && args[0] == "backend" {
|
if len(args) > 0 && args[0] == "backend" {
|
||||||
backendMain(len(args) > 1 && args[1] == "reload")
|
backendMain(len(args) > 1 && args[1] == "reload")
|
||||||
} else if len(args) > 0 && args[0] == "cmd" {
|
|
||||||
handleCommand(args[1:])
|
|
||||||
} else {
|
} else {
|
||||||
frontendMain(len(args) > 0 && args[0] == "skipbackend")
|
frontendMain(len(args) > 0 && args[0] == "frontend")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,6 +55,9 @@ type RPCPacket struct {
|
||||||
|
|
||||||
// backendMain starts all the servers and creates an RPC server to communicate with the frontend
|
// backendMain starts all the servers and creates an RPC server to communicate with the frontend
|
||||||
func backendMain(reload bool) {
|
func backendMain(reload bool) {
|
||||||
|
sigExit := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigExit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
if err := logging.SetOutput(config.LogOutput); err != nil {
|
if err := logging.SetOutput(config.LogOutput); err != nil {
|
||||||
logging.Error("BACKEND", err)
|
logging.Error("BACKEND", err)
|
||||||
}
|
}
|
||||||
|
|
@ -96,8 +100,17 @@ func backendMain(reload bool) {
|
||||||
|
|
||||||
logging.Notice("BACKEND", "Listening on", aurora.BrightCyan(address))
|
logging.Notice("BACKEND", "Listening on", aurora.BrightCyan(address))
|
||||||
|
|
||||||
// Prevent application from exiting
|
common.Ready()
|
||||||
select {}
|
|
||||||
|
// Wait for a signal to shutdown
|
||||||
|
<-sigExit
|
||||||
|
|
||||||
|
err = common.Shutdown()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
(&RPCPacket{}).Shutdown(struct{}{}, &struct{}{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// RPCPacket.NewConnection is called by the frontend to notify the backend of a new connection
|
// RPCPacket.NewConnection is called by the frontend to notify the backend of a new connection
|
||||||
|
|
@ -183,12 +196,13 @@ var (
|
||||||
|
|
||||||
rpcMutex sync.Mutex
|
rpcMutex sync.Mutex
|
||||||
rpcBusyCount sync.WaitGroup
|
rpcBusyCount sync.WaitGroup
|
||||||
|
backendReady = make(chan struct{})
|
||||||
|
|
||||||
connections = map[string]map[uint64]net.Conn{}
|
connections = map[string]map[uint64]net.Conn{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// frontendMain starts the backend process and communicates with it using RPC
|
// frontendMain starts the backend process and communicates with it using RPC
|
||||||
func frontendMain(skipBackend bool) {
|
func frontendMain(noBackend bool) {
|
||||||
// Don't allow the frontend to output to a file (there's no reason to)
|
// Don't allow the frontend to output to a file (there's no reason to)
|
||||||
logOutput := config.LogOutput
|
logOutput := config.LogOutput
|
||||||
if logOutput == "StdOutAndFile" {
|
if logOutput == "StdOutAndFile" {
|
||||||
|
|
@ -203,7 +217,7 @@ func frontendMain(skipBackend bool) {
|
||||||
|
|
||||||
startFrontendServer()
|
startFrontendServer()
|
||||||
|
|
||||||
if !skipBackend {
|
if !noBackend {
|
||||||
go startBackendProcess(false, true)
|
go startBackendProcess(false, true)
|
||||||
} else {
|
} else {
|
||||||
go waitForBackend()
|
go waitForBackend()
|
||||||
|
|
@ -285,11 +299,16 @@ func startBackendProcess(reload bool, wait bool) {
|
||||||
// waitForBackend waits for the backend to start.
|
// waitForBackend waits for the backend to start.
|
||||||
// Expects the RPC mutex to be locked.
|
// Expects the RPC mutex to be locked.
|
||||||
func waitForBackend() {
|
func waitForBackend() {
|
||||||
|
<-backendReady
|
||||||
|
backendReady = make(chan struct{})
|
||||||
|
|
||||||
for {
|
for {
|
||||||
client, err := rpc.Dial("tcp", "localhost:29999")
|
client, err := rpc.Dial("tcp", "localhost:29999")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rpcClient = client
|
rpcClient = client
|
||||||
rpcMutex.Unlock()
|
rpcMutex.Unlock()
|
||||||
|
|
||||||
|
logging.Notice("FRONTEND", "Connected to backend")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -431,19 +450,6 @@ func (r *RPCFrontendPacket) CloseConnection(args RPCFrontendPacket, _ *struct{})
|
||||||
func (r *RPCFrontendPacket) ReloadBackend(_ struct{}, _ *struct{}) error {
|
func (r *RPCFrontendPacket) ReloadBackend(_ struct{}, _ *struct{}) error {
|
||||||
r.ShutdownBackend(struct{}{}, &struct{}{})
|
r.ShutdownBackend(struct{}{}, &struct{}{})
|
||||||
|
|
||||||
// Unlocks the mutex locked by ShutdownBackend
|
|
||||||
startBackendProcess(true, false)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPCFrontendPacket.ShutdownBackend is called by an external program to shutdown the backend
|
|
||||||
func (r *RPCFrontendPacket) ShutdownBackend(_ struct{}, _ *struct{}) error {
|
|
||||||
// Lock indefinitely
|
|
||||||
rpcMutex.Lock()
|
|
||||||
|
|
||||||
rpcBusyCount.Wait()
|
|
||||||
|
|
||||||
err := rpcClient.Call("RPCPacket.Shutdown", struct{}{}, nil)
|
err := rpcClient.Call("RPCPacket.Shutdown", struct{}{}, nil)
|
||||||
if err != nil && !strings.Contains(err.Error(), "An existing connection was forcibly closed by the remote host.") {
|
if err != nil && !strings.Contains(err.Error(), "An existing connection was forcibly closed by the remote host.") {
|
||||||
logging.Error("FRONTEND", "Failed to reload backend:", err)
|
logging.Error("FRONTEND", "Failed to reload backend:", err)
|
||||||
|
|
@ -454,52 +460,28 @@ func (r *RPCFrontendPacket) ShutdownBackend(_ struct{}, _ *struct{}) error {
|
||||||
logging.Error("FRONTEND", "Failed to close RPC client:", err)
|
logging.Error("FRONTEND", "Failed to close RPC client:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlocks the mutex locked by ShutdownBackend
|
||||||
|
startBackendProcess(true, true)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCFrontendPacket.ShutdownBackend is called by an external program to shutdown the backend
|
||||||
|
func (r *RPCFrontendPacket) ShutdownBackend(_ struct{}, _ *struct{}) error {
|
||||||
|
logging.Notice("FRONTEND", "Shutting down backend")
|
||||||
|
|
||||||
|
// Lock indefinitely
|
||||||
|
rpcMutex.Lock()
|
||||||
|
|
||||||
|
rpcBusyCount.Wait()
|
||||||
|
|
||||||
go waitForBackend()
|
go waitForBackend()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleCommand is used to send a command to the backend
|
// RPCFrontendPacket.Ready is called by the backend to indicate it is ready to accept connections
|
||||||
func handleCommand(args []string) {
|
func (r *RPCFrontendPacket) Ready(_ struct{}, _ *struct{}) error {
|
||||||
if len(args) < 2 {
|
close(backendReady)
|
||||||
fmt.Printf("Usage: %s cmd <f|b> <command...>\n", os.Args[0])
|
return nil
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var client *rpc.Client
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if args[0] == "f" {
|
|
||||||
client, err = rpc.Dial("tcp", "localhost:29998")
|
|
||||||
} else if args[0] == "b" {
|
|
||||||
client, err = rpc.Dial("tcp", "localhost:29999")
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Unknown command type: '%s', please supply 'f' or 'b' (for frontend or backend)\n", args[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to connect to RPC server:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
if args[0] == "b" {
|
|
||||||
fmt.Printf("Unknown backend command: '%s'\n", args[1])
|
|
||||||
} else {
|
|
||||||
if args[1] == "backend" {
|
|
||||||
if len(args) > 2 && args[2] == "shutdown" {
|
|
||||||
err = client.Call("RPCFrontendPacket.ShutdownBackend", struct{}{}, nil)
|
|
||||||
} else {
|
|
||||||
err = client.Call("RPCFrontendPacket.ReloadBackend", struct{}{}, nil)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Unknown frontend command: '%s'\n", args[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to send command:", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user