Rust 守护进程最佳实践
Rust Daemon best practices
我在用 Rust 为 unix 套接字服务器创建守护进程时遇到困难。在像 node.js 这样的语言中,我只是产生了一个分离的过程。不过rust好像有点难度
(所有工作均在 unix 环境下进行)
这是一个简单的例子。我写了:
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::{Path};
use std::io::{Read, Write};
use std::{fs};
use std::thread;
fn handle_stream(mut stream: UnixStream) {
loop {
let mut read = [0; 1028];
match stream.read(&mut read) {
Ok(n) => {
if n == 0 {
// connection was closed
println!("connection closed?");
break;
}
let msg = String::from_utf8(read.to_vec()).unwrap();
println!("SERVER: {} received from remote client.", msg);
match stream.write_all(&read[0..n]) {
Ok(_) => {}
Err(e) => println!("Error writing to socket: {}", e),
}
}
Err(err) => {
panic!(err);
}
}
}
println!("SERVER: Ending connection with client.");
}
fn server_start() {
// remove existing sock if exists
let _did_remove = fs::remove_file("/Users/tom/desktop/app.sock");
// socket location
let socket_file = "/Users/tom/desktop/app.sock";
let socket = Path::new(socket_file);
// Bind to socket
let listener = match UnixListener::bind(&socket) {
Err(_) => panic!("failed to bind socket"),
Ok(stream) => stream,
};
println!("Server started, waiting for clients");
for conn in listener.incoming() {
match conn {
Ok(stream) => {
// spawn a new thread for each incoming
thread::spawn(|| handle_stream(stream));
}
Err(err) => {
println!("Error connecting to client: {}", err);
break;
}
}
}
}
fn main() {
server_start();
}
根据评论中的消息,我决定使用 systemd 服务而不是创建自己的守护进程。这似乎是管理后台任务的理想方式。我已经编辑了顶部代码,以便它对答案有意义。
Systemd - linux
您需要创建一个 .service
文件并将其放在您的 systemd 守护程序目录中。例如:/etc/systemd/system/test.service
然后更新文件权限:
sudo chmod 644 /etc/systemd/system/test.service
开始您的服务:
sudo systemctl start service_name
服务代码:
[Unit]
Description=Test service
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=username
ExecStart=/usr/bin/env test
[Install]
WantedBy=multi-user.target
Launchctl - macOS
对于 macOS,我们需要创建一个 .plist
文件并将其放在启动守护程序目录中。例如:/Library/LaunchDaemons/test.plist
接下来更新文件的权限:
sudo chown root:wheel /Library/LaunchDaemons/com.test.daemon.plist
加载守护进程:
launchctl load /Library/LaunchDaemons/com.test.daemon.plist
启动守护进程:
launchctl start /Library/LaunchDaemons/com.test.daemon
Plist代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.test.daemon</string>
<key>ServiceDescription</key>
<string>Test Server</string>
<key>ProgramArguments</key>
<array>
<string>/Users/tom/desktop/test/target/debug/test</string>
</array>
<key>RunAtLoad</key>
<false/>
</dict>
</plist>
我在用 Rust 为 unix 套接字服务器创建守护进程时遇到困难。在像 node.js 这样的语言中,我只是产生了一个分离的过程。不过rust好像有点难度
(所有工作均在 unix 环境下进行)
这是一个简单的例子。我写了:
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::{Path};
use std::io::{Read, Write};
use std::{fs};
use std::thread;
fn handle_stream(mut stream: UnixStream) {
loop {
let mut read = [0; 1028];
match stream.read(&mut read) {
Ok(n) => {
if n == 0 {
// connection was closed
println!("connection closed?");
break;
}
let msg = String::from_utf8(read.to_vec()).unwrap();
println!("SERVER: {} received from remote client.", msg);
match stream.write_all(&read[0..n]) {
Ok(_) => {}
Err(e) => println!("Error writing to socket: {}", e),
}
}
Err(err) => {
panic!(err);
}
}
}
println!("SERVER: Ending connection with client.");
}
fn server_start() {
// remove existing sock if exists
let _did_remove = fs::remove_file("/Users/tom/desktop/app.sock");
// socket location
let socket_file = "/Users/tom/desktop/app.sock";
let socket = Path::new(socket_file);
// Bind to socket
let listener = match UnixListener::bind(&socket) {
Err(_) => panic!("failed to bind socket"),
Ok(stream) => stream,
};
println!("Server started, waiting for clients");
for conn in listener.incoming() {
match conn {
Ok(stream) => {
// spawn a new thread for each incoming
thread::spawn(|| handle_stream(stream));
}
Err(err) => {
println!("Error connecting to client: {}", err);
break;
}
}
}
}
fn main() {
server_start();
}
根据评论中的消息,我决定使用 systemd 服务而不是创建自己的守护进程。这似乎是管理后台任务的理想方式。我已经编辑了顶部代码,以便它对答案有意义。
Systemd - linux
您需要创建一个 .service
文件并将其放在您的 systemd 守护程序目录中。例如:/etc/systemd/system/test.service
然后更新文件权限:
sudo chmod 644 /etc/systemd/system/test.service
开始您的服务:
sudo systemctl start service_name
服务代码:
[Unit]
Description=Test service
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=username
ExecStart=/usr/bin/env test
[Install]
WantedBy=multi-user.target
Launchctl - macOS
对于 macOS,我们需要创建一个 .plist
文件并将其放在启动守护程序目录中。例如:/Library/LaunchDaemons/test.plist
接下来更新文件的权限:
sudo chown root:wheel /Library/LaunchDaemons/com.test.daemon.plist
加载守护进程:
launchctl load /Library/LaunchDaemons/com.test.daemon.plist
启动守护进程:
launchctl start /Library/LaunchDaemons/com.test.daemon
Plist代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.test.daemon</string>
<key>ServiceDescription</key>
<string>Test Server</string>
<key>ProgramArguments</key>
<array>
<string>/Users/tom/desktop/test/target/debug/test</string>
</array>
<key>RunAtLoad</key>
<false/>
</dict>
</plist>