如何编写自动启动服务并监控

wen 实用脚本 2

本文目录导读:

如何编写自动启动服务并监控

  1. Linux 系统服务(Systemd)
  2. Windows 服务
  3. 配置自动重启策略
  4. 高级监控集成
  5. 最佳实践建议

我来为你详细介绍如何编写自动启动服务并实现监控功能,涵盖Linux和Windows两个主要平台。

Linux 系统服务(Systemd)

创建服务单元文件

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment="NODE_ENV=production"
ExecStart=/usr/bin/node /opt/myapp/app.js
Restart=always
RestartSec=10
StartLimitIntervalSec=60
StartLimitBurst=5
# 安全配置
CapabilityBoundingSet=
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.target

监控脚本示例

#!/bin/bash
# /opt/myapp/monitor.sh
# 配置
SERVICE_NAME="myapp"
LOG_FILE="/var/log/myapp-monitor.log"
HEALTH_URL="http://localhost:3000/health"
TIMEOUT=10
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
check_service() {
    # 检查进程是否存在
    if ! systemctl is-active --quiet "$SERVICE_NAME"; then
        log "ERROR: Service $SERVICE_NAME is not running"
        return 1
    fi
    # 检查健康端点
    if [ -n "$HEALTH_URL" ]; then
        if ! curl -s --max-time "$TIMEOUT" "$HEALTH_URL" > /dev/null; then
            log "WARNING: Health check failed for $SERVICE_NAME"
            return 1
        fi
    fi
    return 0
}
restart_service() {
    log "Restarting $SERVICE_NAME..."
    systemctl restart "$SERVICE_NAME"
    sleep 5
    # 验证重启成功
    if check_service; then
        log "Service restarted successfully"
        return 0
    else
        log "ERROR: Service restart failed"
        return 1
    }
}
monitor_loop() {
    local failures=0
    local max_failures=3
    while true; do
        if ! check_service; then
            ((failures++))
            if [ "$failures" -ge "$max_failures" ]; then
                restart_service
                failures=0
            fi
        else
            failures=0
        fi
        sleep 30
    done
}
# 主流程
case "$1" in
    start)
        log "Starting monitor..."
        monitor_loop &
        echo $! > /var/run/myapp-monitor.pid
        ;;
    stop)
        kill $(cat /var/run/myapp-monitor.pid)
        ;;
    status)
        check_service && echo "Running" || echo "Stopped"
        ;;
    *)
        echo "Usage: $0 {start|stop|status}"
        ;;
esac

Systemd监控服务

# /etc/systemd/system/myapp-monitor.service
[Unit]
Description=My Application Monitor
After=network.target
Requires=myapp.service
[Service]
Type=forking
ExecStart=/opt/myapp/monitor.sh start
ExecStop=/opt/myapp/monitor.sh stop
PIDFile=/var/run/myapp-monitor.pid
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target

Windows 服务

C# Windows 服务示例

// MyService.cs
using System;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Timers;
public partial class MyService : ServiceBase
{
    private Timer _healthCheckTimer;
    private Process _workerProcess;
    private EventLog _eventLog;
    public MyService()
    {
        InitializeComponent();
        this.ServiceName = "MyAppService";
        _eventLog = new EventLog();
        if (!EventLog.SourceExists("MyAppService"))
        {
            EventLog.CreateEventSource("MyAppService", "Application");
        }
        _eventLog.Source = "MyAppService";
        _eventLog.Log = "Application";
    }
    protected override void OnStart(string[] args)
    {
        _eventLog.WriteEntry("Service starting...", EventLogEntryType.Information);
        try
        {
            StartWorkerProcess();
            // 设置健康检查定时器
            _healthCheckTimer = new Timer(30000); // 30秒
            _healthCheckTimer.Elapsed += HealthCheckTimer_Elapsed;
            _healthCheckTimer.AutoReset = true;
            _healthCheckTimer.Start();
            _eventLog.WriteEntry("Service started successfully", EventLogEntryType.Information);
        }
        catch (Exception ex)
        {
            _eventLog.WriteEntry($"Failed to start service: {ex.Message}", 
                EventLogEntryType.Error);
            throw;
        }
    }
    protected override void OnStop()
    {
        _eventLog.WriteEntry("Service stopping...", EventLogEntryType.Information);
        _healthCheckTimer?.Stop();
        _healthCheckTimer?.Dispose();
        StopWorkerProcess();
        _eventLog.WriteEntry("Service stopped", EventLogEntryType.Information);
    }
    private void StartWorkerProcess()
    {
        _workerProcess = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "node",
                Arguments = "app.js",
                WorkingDirectory = @"C:\MyApp",
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true
            }
        };
        _workerProcess.Exited += WorkerProcess_Exited;
        _workerProcess.EnableRaisingEvents = true;
        _workerProcess.Start();
    }
    private void StopWorkerProcess()
    {
        if (_workerProcess != null && !_workerProcess.HasExited)
        {
            _workerProcess.Kill();
            _workerProcess.WaitForExit(5000);
        }
    }
    private void WorkerProcess_Exited(object sender, EventArgs e)
    {
        _eventLog.WriteEntry("Worker process exited unexpectedly", 
            EventLogEntryType.Warning);
        // 自动重启
        if (!_workerProcess.HasExited)
        {
            StartWorkerProcess();
        }
    }
    private void HealthCheckTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            // 健康检查逻辑
            if (_workerProcess == null || _workerProcess.HasExited)
            {
                _eventLog.WriteEntry("Health check failed: process not running", 
                    EventLogEntryType.Warning);
                StartWorkerProcess();
            }
            // HTTP健康检查
            using (var client = new System.Net.Http.HttpClient())
            {
                client.Timeout = TimeSpan.FromSeconds(10);
                var response = client.GetAsync("http://localhost:3000/health")
                    .GetAwaiter().GetResult();
                if (!response.IsSuccessStatusCode)
                {
                    _eventLog.WriteEntry($"Health check returned {response.StatusCode}", 
                        EventLogEntryType.Warning);
                }
            }
        }
        catch (Exception ex)
        {
            _eventLog.WriteEntry($"Health check failed: {ex.Message}", 
                EventLogEntryType.Warning);
        }
    }
}

安装服务

# install-service.ps1
$serviceName = "MyAppService"
$servicePath = "C:\MyApp\MyService.exe"
# 检查服务是否存在
if (Get-Service $serviceName -ErrorAction SilentlyContinue) {
    Write-Host "Service already exists, removing..."
    Stop-Service $serviceName -Force
    & "C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe" /u $servicePath
}
# 安装服务
& "C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe" $servicePath
# 配置服务
Set-Service -Name $serviceName -StartupType Automatic
Start-Service $serviceName
Write-Host "Service installed and started successfully"

配置自动重启策略

Linux Systemd 配置

[Service]
Restart=always
RestartSec=10
StartLimitInterval=600
StartLimitBurst=10
# 限制内存使用
MemoryMax=500M
MemoryHigh=400M
# CPU限制
CPUQuota=80%

Windows Service 配置

# 配置服务恢复选项
sc failure $serviceName reset=86400 actions=restart/30000/restart/60000/restart/120000
sc.exe failureflag $serviceName 1

高级监控集成

Prometheus 指标暴露

# metrics.py
from prometheus_client import start_http_server, Gauge, Counter
import psutil
import time
class ServiceMonitor:
    def __init__(self):
        self.service_up = Gauge('service_up', 'Service status')
        self.cpu_usage = Gauge('cpu_usage_percent', 'CPU usage')
        self.memory_usage = Gauge('memory_usage_mb', 'Memory usage')
        self.restart_count = Counter('restart_total', 'Total restarts')
    def collect_metrics(self):
        while True:
            self.cpu_usage.set(psutil.cpu_percent())
            self.memory_usage.set(psutil.virtual_memory().used / 1024 / 1024)
            time.sleep(15)
if __name__ == '__main__':
    monitor = ServiceMonitor()
    start_http_server(8000)
    monitor.collect_metrics()

邮件告警

# alert.py
import smtplib
from email.mime.text import MIMEText
import requests
def send_alert(service_name, error_message):
    msg = MIMEText(f"""
    Service: {service_name}
    Status: DOWN
    Time: {time.ctime()}
    Error: {error_message}
    """)
    msg['Subject'] = f'ALERT: {service_name} is down'
    msg['From'] = 'monitor@example.com'
    msg['To'] = 'admin@example.com'
    with smtplib.SMTP('smtp.example.com') as server:
        server.login('user', 'password')
        server.send_message(msg)

最佳实践建议

  1. 日志管理

    • 使用结构化日志 (JSON格式)
    • 实施日志轮转
    • 集中日志收集
  2. 健康检查

    • 实现多种检查方式 (进程、HTTP、端口)
    • 设置合理的超时时间
    • 避免检查本身成为新故障点
  3. 重启策略

    • 设置最大重启次数防止循环重启
    • 增加指数退避机制
    • 记录重启原因和次数
  4. 监控指标

    • CPU、内存、磁盘使用率
    • 响应时间、错误率
    • 连接数、请求数
  5. 安全考虑

    • 最小权限原则运行服务
    • 使用服务账户而非管理员账户
    • 加密敏感配置

这个方案可以根据你的具体需求调整,确保服务稳定运行并快速响应故障,好的监控不仅要发现问题,还要帮助快速定位和解决问题。

抱歉,评论功能暂时关闭!