命令是干什么的
socket 单元让 systemd 先替服务监听端口,等第一个连接到达时再把服务拉起来,这就是 socket 激活。好处是低频服务不必常驻内存,同时把启动顺序解耦:服务还没就绪时连接会被内核队列缓冲住而不是被拒绝。systemd 把已经建立的监听套接字通过文件描述符交给服务进程,所以服务本身不需要再调 bind,改成从环境变量里取这个描述符即可。
基本信息
| 适用平台 | 所有 systemd 发行版(RHEL 7 起、Debian 9 起、Ubuntu 16.04 起) |
|---|---|
| 单元文件 | myapp.socket 负责监听,myapp.service 或模板 myapp@.service 负责干活 |
| 关键指令 | ListenStream、ListenDatagram、Accept、Service、FileDescriptorName |
| 查看命令 | systemctl list-sockets、systemctl status myapp.socket |
| 应用侧要求 | 不自行 bind,改读 LISTEN_FDS 与 LISTEN_PID,或用 sd_listen_fds |
| 权限要求 | 单元文件写入与管理需要 root |
典型使用场景
- 低频访问的管理接口和健康检查端点,没必要常年常驻占内存
- 老式 inetd 风格的小服务迁移进来,用 Accept 让 systemd 负责收发连接
- 启动竞态解耦:上游还没起来,客户端的连接也不会被直接拒绝
- 小内存机器按需唤醒多个服务,把常驻进程压成有请求才启动
- 服务升级窗口内 socket 继续监听,新连接排队等待而不是失败
- 给只会使用已建立套接字的程序做一个统一入口
socket 与 service 单元
socket 与 service 单元
# /etc/systemd/system/myapp.socket
[Unit]
Description=myapp 按需唤醒的监听套接字
[Socket]
ListenStream=127.0.0.1:8081 # 监听地址与端口
Accept=no # no 表示只监听,连接交给同一个服务实例
FileDescriptorName=myapp # 给套接字起名字,便于应用识别
[Install]
WantedBy=sockets.target # 只启用 socket,服务由它按需拉起
# /etc/systemd/system/myapp.service
[Service]
ExecStart=/usr/local/bin/myapp --listen-fds # 应用需支持从 systemd 继承套接字
# 不要写 WantedBy=multi-user.target,否则开机就常驻,失去按需意义启用与验证步骤
启用与验证步骤
sudo systemctl daemon-reload # 改过单元文件必须重载
sudo systemctl enable --now myapp.socket # 只启用 socket,服务保持未启动
systemctl is-active myapp.service # 首次访问前应为 inactive
curl -s http://127.0.0.1:8081/health # 第一个请求触发服务被拉起
systemctl is-active myapp.service # 再查已变成 active
systemctl list-sockets --no-pager | head # 列出监听中的套接字
systemd-socket-activate -l 8081 /usr/local/bin/myapp # 手工验证应用是否会用继承的 fdlist-sockets 输出逐列读法
| 列名 | 示例 | 含义与注意 |
|---|---|---|
| LISTEN | 127.0.0.1:8081 | 正在监听的地址与端口,方括号包围的 IPv6 表示全部地址 |
| UNIT | myapp.socket | 负责监听的 socket 单元名 |
| ACTIVATES | myapp.service | 连接到来时触发的单元,这一列为空说明单元名没对上 |
Accept=no 适合有 systemd 库支持的应用,一个服务实例处理多个连接;Accept=yes 是 inetd 风格,每个连接拉起一个进程,服务要写成模板单元,连接从标准输入输出进出。
应用侧要表明自己会接管套接字:调用 sd_listen_fds 或直接读 LISTEN_FDS 与 LISTEN_PID 环境变量。不读这两个变量又自行绑定,就会出现端口被占用的报错。
改了 ListenStream 之后必须 daemon-reload;已经运行中的单元还要 restart 才会按新地址监听,只改文件不重载等于没改。