Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4927bdf019 | |||
| 5d2e13db0c | |||
| d494093850 |
@@ -4,3 +4,5 @@ b43527c
|
|||||||
83acd30
|
83acd30
|
||||||
b2baee0
|
b2baee0
|
||||||
13c4c19
|
13c4c19
|
||||||
|
5668d65
|
||||||
|
d494093
|
||||||
+15
-12
@@ -78,29 +78,32 @@ def main_once
|
|||||||
(0...TRY_TIMES).each do
|
(0...TRY_TIMES).each do
|
||||||
chkrslt = ServerStatusDetector::MinecraftServer.self_diag
|
chkrslt = ServerStatusDetector::MinecraftServer.self_diag
|
||||||
chkrslts.append(chkrslt)
|
chkrslts.append(chkrslt)
|
||||||
|
sleep 0.5 unless chkrslt[:type] # 只在失败时等待后再重试
|
||||||
end
|
end
|
||||||
if chkrslts.all? { |chkrslt_in| chkrslt_in[:type] }
|
if chkrslts.all? { |chkrslt_in| chkrslt_in[:type] }
|
||||||
MethodExecutor.record('debug', 'mainloop', 'Server health check passed.')
|
MethodExecutor.record('debug', 'mainloop', 'Server health check passed.')
|
||||||
|
MethodExecutor.reset_rst_counter
|
||||||
else
|
else
|
||||||
chkrslts.each do |result|
|
chkrslts.each do |result|
|
||||||
MethodExecutor.record('warn', 'mainloop', "Server health check FAILED: #{result[:message]}")
|
MethodExecutor.record('warn', 'mainloop', "Server health check FAILED: #{result[:message]}")
|
||||||
end
|
end
|
||||||
MethodExecutor.record('info', 'mainloop', "Checked #{TRY_TIMES} time.")
|
MethodExecutor.record('info', 'mainloop', "Checked #{TRY_TIMES} time.")
|
||||||
if chkrslts.any? { |chkrslt_in| chkrslt_in[:operation].include?('Record') }
|
if chkrslts.any? { |chkrslt_in| (chkrslt_in[:operation] || []).include?('Record') }
|
||||||
MethodExecutor.record('warn', 'mainloop', 'Action: Record -> Server died!')
|
MethodExecutor.record('warn', 'mainloop', 'Action: Record -> Server died!')
|
||||||
if chkrslts.any? { |chkrslt_in| chkrslt_in[:message] == 'Server died for unknown reason, and will be restarted.' }
|
if chkrslts.any? { |chkrslt_in| (chkrslt_in[:operation] || []).include?('ForceResetServerProcess') }
|
||||||
MethodExecutor.record('info', 'mainloop', 'Action: ForceResetServerProcess triggered.')
|
MethodExecutor.record('info', 'mainloop', 'Action: ForceResetServerProcess triggered.')
|
||||||
MethodExecutor.reset_minecraft_server
|
if MethodExecutor.reset_minecraft_server
|
||||||
# 重启后等待服务器启动(MC服务器启动通常需要30秒~2分钟)
|
# 重启后等待服务器启动(MC服务器启动通常需要30秒~2分钟)
|
||||||
cooldown_beg = Time.now
|
cooldown_beg = Time.now
|
||||||
MethodExecutor.record('info', 'mainloop', "Cooling down #{RST_COOLDOWN_DURATION}s for server startup...")
|
MethodExecutor.record('info', 'mainloop', "Cooling down #{RST_COOLDOWN_DURATION}s for server startup...")
|
||||||
# rubocop:disable Metrics/BlockNesting
|
# rubocop:disable Metrics/BlockNesting
|
||||||
while Time.now < cooldown_beg + RST_COOLDOWN_DURATION
|
while Time.now < cooldown_beg + RST_COOLDOWN_DURATION
|
||||||
sleep 1
|
sleep 1
|
||||||
SystemdNotifier.notify('WATCHDOG=1')
|
SystemdNotifier.notify('WATCHDOG=1')
|
||||||
|
end
|
||||||
|
# rubocop:enable Metrics/BlockNesting
|
||||||
|
MethodExecutor.record('info', 'mainloop', 'Cooldown finished, resuming monitoring.')
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/BlockNesting
|
|
||||||
MethodExecutor.record('info', 'mainloop', 'Cooldown finished, resuming monitoring.')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+10
-4
@@ -66,13 +66,13 @@ class MethodExecutor
|
|||||||
# rubocop:enable Metrics/MethodLength
|
# rubocop:enable Metrics/MethodLength
|
||||||
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||||
|
|
||||||
# @return [void]
|
# @return [Boolean] true if a restart was attempted, false if recovered from idle
|
||||||
def self.reset_minecraft_server
|
def self.reset_minecraft_server
|
||||||
record('info', 'reset', "Attempt ##{@@rst_counter + 1} to reset Minecraft server.")
|
record('info', 'reset', "Attempt ##{@@rst_counter + 1} to reset Minecraft server.")
|
||||||
|
|
||||||
if @@rst_counter >= RST_COUNTS_LIMIT
|
if @@rst_counter >= RST_COUNTS_LIMIT
|
||||||
record('error', 'reset',
|
record('error', 'reset',
|
||||||
"Tried #{RST_COUNTS_LIMIT} times and failed, now I'll idle until server is manually restarted.")
|
"Tried #{RST_COUNTS_LIMIT} times and failed, now I'll idle until server is manually recovered.")
|
||||||
loop do
|
loop do
|
||||||
sleep 10
|
sleep 10
|
||||||
SystemdNotifier.notify('WATCHDOG=1') if defined?(SystemdNotifier)
|
SystemdNotifier.notify('WATCHDOG=1') if defined?(SystemdNotifier)
|
||||||
@@ -80,7 +80,7 @@ class MethodExecutor
|
|||||||
|
|
||||||
record('info', 'reset', 'Server recovered! Resuming normal monitoring.')
|
record('info', 'reset', 'Server recovered! Resuming normal monitoring.')
|
||||||
@@rst_counter = 0
|
@@rst_counter = 0
|
||||||
return
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -106,7 +106,6 @@ class MethodExecutor
|
|||||||
elapsed = (Time.now - tick_beg).round(2)
|
elapsed = (Time.now - tick_beg).round(2)
|
||||||
if status.success?
|
if status.success?
|
||||||
record('info', 'reset', "'systemctl restart' succeeded in #{elapsed}s.")
|
record('info', 'reset', "'systemctl restart' succeeded in #{elapsed}s.")
|
||||||
@@rst_counter = 0 # 成功时重置计数器
|
|
||||||
else
|
else
|
||||||
record('error', 'reset',
|
record('error', 'reset',
|
||||||
"'systemctl restart' failed with exitcode #{status.exitstatus} after #{elapsed}s.")
|
"'systemctl restart' failed with exitcode #{status.exitstatus} after #{elapsed}s.")
|
||||||
@@ -128,6 +127,13 @@ class MethodExecutor
|
|||||||
end
|
end
|
||||||
# rubocop:enable Metrics/BlockLength
|
# rubocop:enable Metrics/BlockLength
|
||||||
end
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Resets the restart counter when server health check passes.
|
||||||
|
# @return [void]
|
||||||
|
def self.reset_rst_counter
|
||||||
|
@@rst_counter = 0
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
||||||
# rubocop:enable Style/ClassVars
|
# rubocop:enable Style/ClassVars
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# Sends keep-alive notifications (including WATCHDOG=1) to systemd
|
# Sends keep-alive notifications (including WATCHDOG=1) to systemd
|
||||||
# via the NOTIFY_SOCKET datagram socket, enabling service supervision.
|
# via the NOTIFY_SOCKET datagram socket, enabling service supervision.
|
||||||
class SystemdNotifier
|
class SystemdNotifier
|
||||||
|
require 'socket'
|
||||||
require_relative 'method_executor'
|
require_relative 'method_executor'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
-9
@@ -18,11 +18,11 @@ require 'net/http'
|
|||||||
require 'uri'
|
require 'uri'
|
||||||
|
|
||||||
# @type const RELEASE_SERIAL_CODE: String
|
# @type const RELEASE_SERIAL_CODE: String
|
||||||
RELEASE_SERIAL_CODE = '13c4c19git '
|
RELEASE_SERIAL_CODE = 'd494093'
|
||||||
# @type const CHANNEL: String
|
# @type const CHANNEL: String
|
||||||
CHANNEL = 'dev'
|
CHANNEL = 'dev'
|
||||||
# @type const VERSION_LIST_URL: String
|
# @type const VERSION_LIST_URL: String
|
||||||
VERSION_LIST_URL = "https://gitee.com/api/v5/repos/SCU_team/mine-sentinel/contents/APP_VERSIONS_LIST?ref=#{CHANNEL}"
|
VERSION_LIST_URL = "https://gitee.com/SCU_team/mine-sentinel/raw/#{CHANNEL}/APP_VERSIONS_LIST"
|
||||||
# @type const RECURSIVE_RETRY_DEPTH: Integer
|
# @type const RECURSIVE_RETRY_DEPTH: Integer
|
||||||
RECURSIVE_RETRY_DEPTH = 3
|
RECURSIVE_RETRY_DEPTH = 3
|
||||||
|
|
||||||
@@ -32,14 +32,21 @@ class UpdateManager
|
|||||||
def self.fetch
|
def self.fetch
|
||||||
MethodExecutor.record('debug', 'updates', "Fetching version list from #{VERSION_LIST_URL}")
|
MethodExecutor.record('debug', 'updates', "Fetching version list from #{VERSION_LIST_URL}")
|
||||||
uri = URI(VERSION_LIST_URL)
|
uri = URI(VERSION_LIST_URL)
|
||||||
response = Net::HTTP.get_response(uri)
|
5.times do # 跟随重定向(Gitee raw URL → raw.giteeusercontent.com)
|
||||||
case response.code.to_i
|
response = Net::HTTP.get_response(uri)
|
||||||
when 200
|
case response.code.to_i
|
||||||
MethodExecutor.record('debug', 'updates', 'Version list fetched successfully.')
|
when 200
|
||||||
response.body
|
MethodExecutor.record('debug', 'updates', 'Version list fetched successfully.')
|
||||||
else
|
return response.body
|
||||||
raise "Unexpected HTTP response code: #{response.code}"
|
when 301, 302, 307, 308
|
||||||
|
uri = URI(response['location'])
|
||||||
|
MethodExecutor.record('debug', 'updates', "Following redirect to #{uri}")
|
||||||
|
next
|
||||||
|
else
|
||||||
|
raise "Unexpected HTTP response code: #{response.code}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
raise 'Too many redirects'
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
MethodExecutor.record('error', 'updates', "Failed to fetch version list: #{e}")
|
MethodExecutor.record('error', 'updates', "Failed to fetch version list: #{e}")
|
||||||
nil
|
nil
|
||||||
|
|||||||
Reference in New Issue
Block a user