<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Socket · XB 现场笔记</title><link>https://www.xbcatm.com/zh/tags/socket/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://www.xbcatm.com/zh/tags/socket/index.xml" rel="self" type="application/rss+xml"/><item><title>socket 激活让服务按需启动</title><link>https://www.xbcatm.com/zh/posts/systemd-socket-activation-ka/</link><pubDate>Tue, 01 Feb 2028 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/posts/systemd-socket-activation-ka/</guid><description>一个套接字单元先监听，等客户端连上才唤醒对应服务，于是闲着的守护进程不必空转吃 CPU。</description><content:encoded>&lt;p&gt;端口永远就绪，而进程只在有人敲门时才起来，这正是少用却沉重的服务该有的妙招。每个闲置守护进程都在花钱：常驻内存、周期性唤醒、补丁面、日志噪音。一台机器上几十个不常用的服务加起来，税交了不少，事一件没干。套接字激活把交易反过来——内核拿着监听套接字，守护进程出生之前它就归系统服务管理器所有，第一个带着真活的连接到了，服务进程才出生。&lt;/p&gt;
&lt;p&gt;交接的顺滑让一切透明。客户端照常连端口，不知道家里没人；管理器拉起服务，把已经接受的连接递进去，第一个客户端都不必等两遍。做对了，按需启动和常开除了账单看不出区别——除了首击延迟，这是唯一要诚实预算的成本。&lt;/p&gt;
&lt;h2 id="让监听器先等着"&gt;让监听器先等着&lt;/h2&gt;
&lt;p&gt;systemd 在服务之前先把 socket 打开，第一个连接到来时才启动单元，并把收到的文件描述符交过去。把 ListenStream 配好一次，让 socket 在任何守护进程起来之前就先诞生并绑定好。套接字单元和服务单元按名配对，一个管监听、一个管干活；默认模式把整个监听套接字交给单个服务实例，按连接派生的模式则给简单请求处理配每连接一个实例。&lt;/p&gt;
&lt;p&gt;分离还顺手解决了开机排序。不用写脆弱的先后依赖：套接字开机早期就在，客户端随便什么顺序起，连不上就等一等、重试一次，而不是直接报错。排队的连接在内核积压里等着，开机竞态变成了队列深度而不是错误。当然积压长度也要调：启动慢的服务遇上开机早期的连接洪峰，溢出起来和普通监听队列一样。&lt;/p&gt;
&lt;h2 id="服务按需唤醒"&gt;服务按需唤醒&lt;/h2&gt;
&lt;p&gt;空闲的守护进程可以一直停着，用每实例的内存与启动周期，换一次交接的开销。所以套接字激活的服务可以一直睡，直到第一个请求值得它跑起来。这套最适合管理工具、老协议网关、定期但罕见的任务——闲忙比按天算、不按分钟算的服务。&lt;/p&gt;
&lt;p&gt;闲时退出补上闭环：服务空闲若干秒自己停，系统在零成本和满能力之间振荡，不用人管。套接字从不关闭，下一个客户端透明触发下一次启动。没有闲时退出，激活只省了开机内存，进程还是越积越多——按需启动加闲时停止成对出现，妙招才变成稳态节省。&lt;/p&gt;
&lt;h2 id="用速度换内存"&gt;用速度换内存&lt;/h2&gt;
&lt;p&gt;代价是第一次访问有一段短暂热身，这适合很少跑的任务，而对始终热呼的路径很浪费。在首次调用的延迟可接受、而内存节省真的有意义时才开启它。动手前两边都量化：从连接到首个响应的冷启动耗时，常开会永远占住的常驻内存。启动三百毫秒换八百内存，大胆开；预热二十秒换五十内存，算了。&lt;/p&gt;
&lt;p&gt;有些负载无论如何都受不了这个间隙。靠健康检查编排的系统会在启动期判端点死亡，负载均衡会摘掉首个慢后端，超时卡得紧的客户端没等服务热完就失败了。这些就让守护进程常开，内存该花就花——或者开机时预热一次，激活只覆盖之后漫长的闲置。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;标志性故障是惊群式启动：监控扫一轮，五十个睡着的服务同时被连醒，同时启动，主机内存告急、负载冲顶，服务大面积变慢。每个激活单独看都合理，合在一起就是踩踏。用错峰检查间隔、按服务的启动限制、足够短的闲时退出，把稳态重叠压小。测试激活要在监控负载下测，不只拿一条手工请求测。&lt;/p&gt;
&lt;p&gt;第二种故障是半吊子适配：服务认定套接字归自己，启动时自己绑端口，和管理器已绑的套接字撞车。被激活的那一刻恰恰启动失败，连接挂住，日志怪地址已被占用。服务必须接受递进来的文件描述符而不是自己绑；上游代码改不动，就写个小垫片继承套接字再拉起守护进程过渡。&lt;/p&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;端到端验证按需行为：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;systemctl status foo.socket foo.service
ss -ltnp | grep &amp;#39;:8080&amp;#39;
curl -m 10 localhost:8080/health; systemctl show foo.service -p ActiveState,ExecMainStartTimestamp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请求之前服务未激活、套接字在监听；请求之后时间戳显示一次由连接触发的新启动。&lt;/p&gt;
&lt;h2 id="延伸阅读"&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.xbcatm.com/zh/notes/restart-policies-need-a-rate-limit/"&gt;重启策略需要一个速率限制&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xbcatm.com/zh/notes/a-maintenance-timer-should-leave-its-last-result-visible/"&gt;维护定时器应让其最近结果可见&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html"&gt;systemd.socket 手册&lt;/a&gt;，套接字单元官方参考，已核验 2026-06-01。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html"&gt;systemd.service 手册&lt;/a&gt;，服务单元行为与闲时退出，已核验 2026-09-05。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>