網站內容更新 (如部落格靜態頁生成、快取重建、搜尋索引重建) 常需定時觸發。傳統作法使用 Windows 工作排程器呼叫 URL,但受限於登入會話、網路權限、錯誤處理。本文介紹開發一個 .NET Framework 2.0 Windows Service,透過 XML 設定檔定義排程 URL,每 20 秒輪詢執行 HTTP 請求,實現穩定、背景、無人值守的網站自動更新機制。
System.Timers.Timer (20 秒間隔) + System.Net.WebClient / HttpWebRequest + System.Xml.XmlDocument 解析設定。urls.xml 獨立於程式碼,支援熱更新 (服務偵測檔案變更可自動重載)。<?xml version="1.0" encoding="utf-8"?>
<root>
<Urls>
<item url="https://www.example.com/cron/generate_static" time="03:59" />
<item url="https://www.example.com/cron/rebuild_index" time="11:59" />
<item url="https://www.example.com/cron/clear_cache" time="13:59" />
<item url="https://www.example.com/cron/daily_report" time="15:59" />
<item url="https://www.example.com/cron/backup_db" time="16:59" />
</Urls>
</root>
url:完整 HTTP/HTTPS 端點,可攜帶查詢字串 (如 ?action=rebuild&type=full)。time:每日觸發時間 (HH:mm),精確到分鐘。同一分鐘內僅允許一個任務。using System;
using System.Configuration;
using System.Net;
using System.Timers;
using System.Xml;
namespace WebsiteAutoUpdater
{
public partial class UpdaterService : ServiceBase
{
private Timer _timer;
private XmlNodeList _urlNodes;
private string _configPath;
public UpdaterService()
{
InitializeComponent();
_configPath = AppDomain.CurrentDomain.BaseDirectory + ConfigurationManager.AppSettings["ConfigFile"];
}
protected override void OnStart(string[] args)
{
LoadConfig();
_timer = new Timer(20000); // 20 秒
_timer.Elapsed += Timer_Elapsed;
_timer.AutoReset = true;
_timer.Enabled = true;
Log("Service started.");
}
protected override void OnStop()
{
_timer.Enabled = false;
_timer.Dispose();
Log("Service stopped.");
}
private void LoadConfig()
{
var xml = new XmlDocument();
xml.Load(_configPath);
_urlNodes = xml.SelectNodes("//root/Urls/item");
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
var now = DateTime.Now;
var currentTime = now.ToString("HH:mm");
foreach (XmlNode node in _urlNodes)
{
var taskTime = node.Attributes["time"]?.Value;
if (taskTime == currentTime)
{
var url = node.Attributes["url"]?.Value;
if (!string.IsNullOrEmpty(url))
{
TriggerUrl(url);
}
}
}
// 熱重載設定檔 (可選)
if (File.GetLastWriteTime(_configPath) > _lastConfigLoad)
LoadConfig();
}
private void TriggerUrl(string url)
{
try
{
using (var client = new WebClient())
{
client.Headers.Add("User-Agent", "WebsiteAutoUpdater/1.0");
var response = client.DownloadString(url);
Log($"SUCCESS {DateTime.Now:yyyy-MM-dd HH:mm:ss} | {url} | {response.Substring(0, Math.Min(200, response.Length))}");
}
}
catch (WebException ex)
{
var statusCode = ((HttpWebResponse)ex.Response)?.StatusCode ?? HttpStatusCode.InternalServerError;
Log($"ERROR {DateTime.Now:yyyy-MM-dd HH:mm:ss} | {url} | HTTP {(int)statusCode} {statusCode} | {ex.Message}");
}
catch (Exception ex)
{
Log($"EXCEPTION {DateTime.Now:yyyy-MM-dd HH:mm:ss} | {url} | {ex.Message}");
}
}
private void Log(string message)
{
var logPath = AppDomain.CurrentDomain.BaseDirectory + "updater_log.txt";
File.AppendAllText(logPath, message + Environment.NewLine);
}
}
}
WebsiteUpdater.exe。C:\Services\WebsiteUpdater\,放入 exe 與 urls.xml。App.config 設定 ConfigFile 指向 urls.xml。sc create WebsiteUpdater binPath= "C:\Services\WebsiteUpdater\WebsiteUpdater.exe" start= auto obj= "NT AUTHORITY\LocalSystem" displayname= "Website Auto Updater"sc start WebsiteUpdater 或 services.msc 中啟動。updater_log.txt 確認心跳記錄 (Heart Beat) 與任務觸發記錄。| 功能 | 實作方式 |
|---|---|
| 設定檔熱重載 | FileSystemWatcher 監聽 urls.xml 變更 → 重新載入 XmlDocument |
| 失敗重試機制 | 記錄失敗次數,指數退避重試 (1分、5分、15分、1時),超過閾值發送郵件告警 |
| 執行狀態儀表板 | 內建輕量 HTTP 端點 (HttpListener) 提供 JSON 狀態 API,供 Grafana/Prometheus 抓取 |
| 支援 POST/JSON 請求 | XML 擴充 method、headers、body 屬性,HttpWebRequest 完整控制 |
| 多執行緒並行 | Task.Run / Parallel.ForEach 處理同分鐘多任務 (需修改同一分鐘單任務限制) |
| Windows 事件日誌整合 | EventLog.WriteEntry 寫入 Application Log,支援事件檢視器篩選 |
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| Windows Service (本文) | 背景無人值守、開機自啟、完整 .NET 生態、狀態持久化、安全性高 | 需開發/編譯/部署、安裝需管理員權限 | 企業正式環境、長期穩定運行、需複雜邏輯 (重試、告警、儀表板) |
| 工作排程器 + PowerShell/curl | 零開發、圖形化設定、支援觸發條件豐富 | 受限於使用者登入會話、網路認證處理複雜、錯誤重試弱 | 簡單定時任務、臨時需求、開發環境 |
| Linux Cron + curl/wget | 輕量、原生、極度穩定 | 需 Linux 主機、跨平台部署複雜 | 網站部署在 Linux、團隊熟悉 Shell |
| 第三方排程服務 (Hangfire/Quartz.net) | 功能豐富、儀表板內建、分散式支援 | 學習曲線、額外相依套件 | .NET 應用程式內建排程需求 |
自訂 Windows Service 實現網站自動更新,雖需初期開發投入,但提供了 穩定性、可維護性、擴充性 的最佳平衡。XML 設定檔分離業務邏輯與排程配置,配合 20 秒輪詢心跳機制,可滿足分鐘級精度的定時觸發需求。建議從最小可行版本 (核心 Timer + WebClient + XML + Log) 開始,逐步加入熱重載、重試、告警、儀表板等企業級功能。