我想使用 systemd 套接字激活来启动服务,只要在侦听套接字上收到 HTTP 请求即可。传入的消息并不重要,可以由 systemd 或其启动的服务丢弃。我已经能够做到这一点,但 HTTP 发送方会崩溃并出现错误Connection reset by peer
。我想满足发送 HTTP 客户端的要求,正确关闭套接字或 HTTP 交换。
首先,我有一个问题,即是否在文件中使用Accept=yes
或。例如:no
.socket
echo.socket
:
[Unit]
Description=Example echo socket
[Socket]
ListenStream=127.0.0.1:33300
Accept=yes
[Install]
WantedBy=sockets.target
在man systemd.socket
,建议
出于性能原因,建议仅以适合的方式编写新的守护进程
Accept=no
。
但是,我猜想名称Accept=
暗示了套接字上的传入连接是否已被接受(我猜是accept()
在内核级别调用),因为Accept=no
启动的服务需要接受和处理套接字上的连接。例如,请参阅此问题及其答案。如果我可以按照手册页的建议使用Accept=no
,我会非常高兴,但上述问题的答案暗示我需要调用某种 TCP 服务器,这是一个额外的学习曲线和复杂程度,如果可能的话,我可能会避免。(不过,任何类似的答案肯定都是可以接受的。)
因此,假设我使用Accept=yes
,并且我还有[email protected]
:
[Unit]
Description=echo service
[Service]
ExecStart=sh -c 'echo -e "HTTP/1.1 200 OK\nContent-Type: text/plain; charset=utf-8\nConnection: close\n\nOK"'
StandardInput=socket
StandardOutput=socket
发送 HTTP 请求可以激活服务,但是客户端不喜欢它:
$ curl 'http://127.0.0.1:33300'
OK
curl: (56) Recv failure: Connection reset by peer
$ journalctl --user -e
Jun 13 14:02:59 myhost systemd[906]: Started echo service (127.0.0.1:39908).
(例如,也请尝试python -c "import requests; r = requests.get('http://127.0.0.1:33300'); print(r.status_code)"
。)
不过,没有任何 HTTP 的简单程序socat
似乎可以毫无问题地退出。
$ socat - TCP:127.0.0.1:33300
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Connection: close
OK
我不知道这里的问题是什么,是 systemd 是否过于突然地关闭了 HTTP 交换的连接,还是 HTTP 交换缺少一些额外的关闭消息,还是其他什么。而且,如上所述,我仍然对Accept=no
解决方案感到好奇。
问题在于 curl 在发送请求后保持其一侧的连接打开,而由于您从未在“服务器”中读取过该请求,因此这会导致“对等方重置连接”错误。
最简单的解决方案是编写“服务器”,使其在发送回复之前读取请求。我认为最简单的方法是从 shell 切换到
awk
,如下所示:为了便于阅读,awk 脚本的格式如下:
(请注意,在单元文件中我们必须
\n
用替换\\n
;否则 systemd 会\n
用文字换行符替换 。)这将读取请求直到第一个空白行,然后发出响应。它的工作原理正如您所期望的那样:
或者,您可以修改基于 shell 的解决方案以添加
Content-length
标题;这样就可以curl
知道何时收到了整个响应:这也将正常工作而不会出现错误。