16

When writing a program, you will inevitably encounter the need to make a system service. Writing system services under Windows needs to implement some specific interfaces, which is difficult to do. Therefore, many programs adopt similar alternatives-making desktop applications with system taskbar icons. However, the reason why a service is a service is that it has a very important feature: it can start automatically after booting, and does not require a user to log in. Otherwise, you have to log in manually every time you restart, which is a hard work. Of course, Windows can be set to automatically log in, but if it is a hosting server, can you really rest assured to log in automatically?

However, Linux seems to be much more convenient, probably all programs that do not require GUI to run continuously can be made into services.

1. Service in Windows

Let me talk about Windows first. If you are still using Windows XP, then let's not pass it...

1.1. Windows Service Wrapper

[Windows Service Wrapper] is the full name. Its abbreviation WinSW may be more well-known.

WinSW is implemented based on .NET Framework 4.6.1 and .NET 5, so at least Windows 7 SP1 / Windows Server 2008 R2 SP1 can be used. It can encapsulate any Windows program into a Windows service. All you need to do is to write a configuration file and then register a Windows service with WinSW. WinSW download is an independent executable file. Before using it, you need to write a configuration file with the same .xml

For example, Nginx itself does not provide the ability to register as a Windows service. If you need to register as a Windows service, you can use WinSW to encapsulate it. Rename the downloaded WinSW executable file to winsw.exe (you can change it to any name, and the configuration file name can be created with the same name), and place it under the home directory of nginx. The directory structure after the configuration file is created is roughly like this:

[-] nginx
 |-- conf
 |-- ...(其他 nginx 的目录或文件)
 |-- nginx.exe
 |-- winsw.exe
 `-- winsw.xml

winsw.xml is as follows, you can understand it by looking at the comments.

<service>
    <!-- 配置服务名称 nginx-service,显示名称 Nginx Service,以及服务描述 -->
    <id>nginx-service</id>
    <name>Nginx Service</name>
    <description>Nginx Service</description>

    <!-- 服务运行的工作目录,给绝对路径 -->
    <workingdirectory>C:\Local\Nginx</workingdirectory>

    <!-- 服务可执行文件,给绝对路径 -->
    <executable>C:\Local\Nginx\nginx.exe</executable>

    <!-- 停止服务的可执行文件 -->
    <stopexecutable>C:\Local\Nginx\nginx.exe</stopexecutable>
    <!-- 停止服务的参数 -->
    <stoparguments>-s stop</stoparguments>

    <priority>Normal</priority>
    <stoptimeout>15 sec</stoptimeout>
    <stopparentprocessfirst>false</stopparentprocessfirst>

    <!-- 配置服务类型是「自动」启动 -->
    <startmode>Automatic</startmode>
    <waithint>15 sec</waithint>
    <sleeptime>1 sec</sleeptime>

    <!-- 将服务的控制台输出(标准输出/错误输出)写入日志 -->
    <!-- 其中 %BASE% 是指 winsw.exe 所在目录 -->
    <!-- 参考:https://github.com/winsw/winsw/blob/master/doc/loggingAndErrorReporting.md -->
    <logpath>%BASE%\logs</logpath>
    <log mode="roll-by-time">
        <pattern>yyyyMMdd</pattern>
    </log>
</service>

This configuration creates a nginx-service , and its display name in the Windows "Service ( services.msc )" is Nginx Service . When starting the service, run nginx.exe directly to start it, which is a program that will execute the console; and to stop the service, run nginx.exe -s stop . The executable program and parameters are configured in <stopexecutable> and <stoparguments> , respectively. It is not difficult to infer, if Parameters are required to start the service, which are configured in <arguments> .

The detailed configuration can be found in the XML configuratoin file in the github repository. You can also find some examples .

After the configuration is complete, run winsw.exe install to install it as a Windows service. After the installation is complete, you can use the winsw.exe start command to start the service, you can also go to the Windows Service Manager to start, or use the net start command to start. github repository Home of Usage section complete command descriptions.

1.2. Write your own using .NET Framework/Core/5

It is relatively easy to write a service in .NET, because there are ready-made packages (components) available: NuGet Gallery | Microsoft.Extensions.Hosting.WindowsServices , officially produced. It needs to depend on at least two packages:

After the components are introduced, only a small amount of code is needed to make the current .NET Console Application a service program that supports the Windows service interface.

// Program.cs

class Program {
    static async Task<int> Main(string[] args) {
        await CreateHostBuilder(args).Build().RunAsync();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) {
        return Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) => {
                services.AddHostedService<DaemonService>();
            })
            .UseWindowsService();
    }
}

Note AddHostedService<DaemonServce> , where DaemonServce is a self-implemented service business class with free naming, but it needs to Microsoft.Extensions.Hosting.BackgroundService inherited from 06180788aab345

class DaemonService : BackgroundService {
    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        // TODO 提供服务内容的代码
    }
}

The business code of the service is usually continuously running or monitoring code. If it is a planned/periodic task, you can consider using Quartz to achieve.

After the program is completed, you can use the sc command provided by Windows to register/deregister the service. Assuming that the generated program is MyService.exe , the commands for registering, configuring and starting the service are as follows:

sc create "my-service" binPath="C:\MyService\MyService.exe --service"
sc config "my-service" start= auto
sc start "my-service"

Note: The absolute path should be given in binPath

2. Do Systemd service in Ubuntu

There are many types of services under Linux. Recently, Ubuntu is mainly used, so the Systemd service under Ubuntu is used.

Suppose we have written an ASP.NET application of .NET 5, placed in /app/my-web/ , the main file is MyWeb.dll . If you start this Web with the command line, it should be

cd /app/my-web
dotnet MyWeb.dll
Note: You need to prepare the running environment of .NET 5 in advance. You can refer to to install .NET on | Microsoft Docs 16180788aab431.

Next is to write the Systemd service configuration. The configuration file name starts from my-web.service and is placed in the /etc/systemd/system directory. The content (including notes) is as follows:

[Unit]
# 服务说明
Description=My Web Application
# 在启动网络服务之后启动
After=network.target

[Service]
# 总是重启(无论什么原因结束都会立即重启)
Restart=always
RestartSec=10
# 工作目录
WorkingDirectory=/app/my-web
# 启动服务的命令
ExecStart=/usr/bin/dotnet MyWeb.dll
# 通过杀主进程来结束服务
ExecStop=/bin/kill -HUP $MAINPID
TimeoutStopSec=5
KillMode=mixed
SyslogIdentifier=my-web
# 指定运行此服务的用户,涉及到目录访问权限等问题
User=james

[Install]
WantedBy=multi-user.target

After the configuration is completed, the service cannot be started immediately, and systemd needs to reload the configuration before starting the service:

sudo systemctl daemon-reload
sudo systemctl start my-web

By the way, let me introduce again. If you want to automatically restart after the content is released, you need to add two configuration files, one .path monitor changes, and one .service to restart my-web :

  • restart-my-web.path
[Path]
# 监控主文件 MyWeb.dll 的变动,如果有变动会触发 restart-my-web.service 启动
PathModified=/app/my-web/MyWeb.dll

[Install]
WantedBy=multi-user.target
  • restart-my-web.service
[Unit]
Description=My Web Restarter
After=network.target

[Service]
Type=oneshot
# 防抖,60 秒内只启动 1 次
ExecStartPre=/bin/sleep 60
# 重启 my-web.service
ExecStart=/bin/systemctl restart my-web.service

[Install]
WantedBy=multi-user.target

3. A little summary

It is not difficult to provide services. The only way to write code above is achieved by out-of-the-box components. But having said that, it is not difficult to do service, and there are still many things to consider when doing service design. for example

  • How to monitor the status of the service? —— Process monitoring, heartbeat check...
  • How to analyze errors in the service? —— System log
  • How to provide GUI to manage services? —— Web or other UI to interact with the service process (process communication, management API, etc.)
  • ……

Since it is not difficult to provide services, don't be too entangled in how to "do" (provide) the service, but more entangled in how to do (design) the service.


边城
59.8k 声望29.6k 粉丝

一路从后端走来,终于走在了前端!