/// <summary> /// Queue initial heartbeats to active servers /// </summary> public void QueueInitialHeartbeats() { DateTime Now = DateTime.UtcNow; uint HeartbeatsStarted = 0; lock (ActiveServerTable) { foreach (var Pair in ActiveServerTable) { NWGameServer Server = Pair.Value; lock (Server) { if (!Server.Online) { continue; } Server.InitialHeartbeat = true; Server.StartHeartbeat(); HeartbeatsStarted += 1; } } } Logger.Log(LogLevel.Normal, "NWServerTracker.QueueInitialHeartbeats(): Queued {0} initial server heartbeat requests.", HeartbeatsStarted); PendingGameServersSweepTimer.Start(); ScavengerSweepTimer.Start(); BlacklistSweepTimer.Start(); }
/// <summary> /// This method disables future heartbeats and flushes any servers that /// may be in the heartbeat path out of that code path before further /// forward progress is allowed. /// </summary> public void DrainHeartbeats() { // // Prevent new requestors from spinning up new heartbeat requests. // HeartbeatsEnabled = false; // // Flush any in-flight requestors out of the heartbeat path by // ensuring that they have released synchronization. // Monitor.Enter(HeartbeatLock); Monitor.Exit(HeartbeatLock); // // Stop all of the active timers. New timers that have elapsed // will have already completed starting the timer again or will now // observe that future timer restarts are already forbidden. // PendingGameServersSweepTimer.Stop(); ScavengerSweepTimer.Stop(); BlacklistSweepTimer.Stop(); }
/// <summary> /// This timer callback runs when the scavenge sweep timer elapses. /// Its purpose is to cycle through offline game_servers records in the /// database, which have had a heartbeat more recent than /// ScavengerTimeSpan since the current time. Such servers are then /// re-pinged in a low frequency interval so as to allow servers that /// had gone offline for a long period of time (but had been returned /// to service afterwards) to be automatically re-listed eventually. /// </summary> /// <param name="sender">Unused.</param> /// <param name="e">Unused.</param> private void ScavengerSweepTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { try { bool Processed = false; DateTime Now = DateTime.UtcNow; string Query = String.Format( @"SELECT `game_server_id`, `server_address` FROM `game_servers` WHERE `product_id` = {0} AND `game_server_id` > {1} AND `online` = false AND `last_heartbeat` >= '{2}' GROUP BY `game_server_id` ORDER BY `game_server_id` LIMIT 50", MasterServer.ProductID, ScavengeServerId, MasterServer.DateToSQLDate(Now.Subtract(ScavengerTimeSpan))); // // Examine each server record returned and, if the server is // still offline, enqueue a ping to it. Advance the iterator // to the next server as each server is processed. // using (MySqlDataReader Reader = MasterServer.ExecuteQuery(Query)) { while (Reader.Read()) { uint ServerId = Reader.GetUInt32(0); IPEndPoint ServerAddress = ConvertServerHostnameToIPEndPoint(Reader.GetString(1)); if (ServerId > ScavengeServerId) { ScavengeServerId = ServerId; } Processed = true; if (ServerAddress == null) { continue; } NWGameServer Server = LookupServerByAddress(ServerAddress, false); if (Server == null || Server.Online == false) { MasterServer.SendServerInfoRequest(ServerAddress); } } } // // If no servers were processed, then the end of the iterator // list must have been reached. Re-start the sweep cycle at // the beginning next time. // if (Processed == false) { ScavengeServerId = 0; } } catch (Exception ex) { Logger.Log(LogLevel.Error, "NWServerTracker.ScavengeSweepTimer_Elapsed(): Excepton processing scavenge server list: {0}", ex); } lock (HeartbeatLock) { if (!HeartbeatsEnabled) { return; } ScavengerSweepTimer.Start(); } }