当使用systemd
按需启动某套接字进程后,其图示大致如下:
当需要访问该服务时候,systemd
会接收请求流量,而后启动后端真实的服务,最后转发该流量,并且关闭原始套接字,图示如下:
实现一个socket
步骤
所谓的按需启动,其实是systemd
下的socket
配置单元,其命名规则以.socket
为后缀,主要服务于套接字模式的启动。
我们配置socket
后,systemd
会替我们监听端口,当访问该端口的时候,systemd
会启动后端实例,并且将请求转发至该实例,后端完成需求后,由systemd
销毁该实例。已达到按需启动的效果。
注意,我们想接入socket
,编写的业务要符合该就不能再对外监听套接字了,虽然systemd
不会卡这个,但是不符合按需启动的逻辑,所以,我们自己写的业务,想要接入systemd
需要修改其代码,修改为符合systemd socket
规则的,换句话说,服务器监听由systemd
来替我们做了,我们只用关心其业务逻辑即可。甚至要从文件或者标准输入中获取客户端发送的数据。
创建简单的socket
所以,我们不仅需要配置socket
,还需要配置其后端模板实例,先来创建一个socket
,例如:
[Unit]
Description=a test sample web socket
[Socket]
ListenStream=9999
Accept=yes
我们将其命名为 testSampleWeb.socket
,并且存放到/usr/lib/systemd/system
目录下。
这段配置文件意思是:
Description
: 该服务的简介ListenStream
: 对外监听的tcp
端口Accept
: 为true
表示为每个连接传入单独的服务
在此,我们仅需reload
下systemd
配置档:
systemctl daemon-reload
在使用systemctl status testSampleWeb.socket
查看当前状态:
可以看到,目前状态是关闭状态的,可以使用start
操作先开启该服务:
此时,若查看9999
端口占用情况,会发现是systemd
占用的,且pid
为1,例如:
创建后端实例
注意,此时我们还没有编写属于该socket
背后的实例,所以,请求该端口,会抛错,例如:
而在systemd
日志中,会详细的描述,找不到该socket
的后端实例配置,如:
所以,我们需要在此目录下,创建后端实例配置文件@.service
结尾,如socket
配置文件为: testSampleWeb.socket
, 则该实例service
的配置文件是[email protected]
,其内容如下:
[Unit]
Description=sample python webs %i
[Service]
ExecStart=-/usr/bin/python3 /root/sampleWeb.py
StandardInput=socket
这个要注意,为什么不是创建.service
结尾的配置呢,这是因为此前创建socket
的时候,Accept
设置为True
,所以要创建以@.service
结尾的配置文件。
该配置的含义是:
ExecStart
: 执行的命令StandardInput
: 标准输入方式为socket
接着,我们需要编写该后端服务代码,其内容如下:
python 代码解读 复制代码import sys
msgData = ""
while True:
datas = sys.stdin.read(1)
msgData += datas
if msgData.endswith("\r\n\r\n"):
break
httpInfo = msgData.split("\r\n")
rHeader = httpInfo[0]
httpDicts = {}
if 1 < len(httpInfo):
httpDicts = dict([x.split(":",1) for x in httpInfo[1:] if ":" in x])
contentLen = 0
if "Content-Length" in httpDicts:
contentLen = int(httpDicts["Content-Length"])
body = sys.stdin.read(contentLen)
sys.stdout.write("请求报文为:\n")
sys.stdout.write("\n请求行:\n" + str(httpInfo[0]))
sys.stdout.write("\n\n首部行:\n")
[print(k,":",httpDicts[k]) for k in httpDicts]
sys.stdout.write("\n\n报文主体:\n")
sys.stdout.write(body)
sys.stdout.write("\n\n")
上述代码,是一个简单的拆分http
报文协议的代码,你可能很好奇,为什么没有调用socket
呢?从哪儿来的客户端信息呢?正如上面所述,systemd socket
已经帮我们把台架子搭好了,我们仅需要从标准输入流获取数据,要想发送给客户端,只需要往标准输出吐出信息即可。
测试
需要先加载配置文件,而后重启socket
,命令如下:
systemctl daemon-reload
systemctl start testSampleWeb.socket
使用netstat
查看端口为9999
的监听信息
netstat -tulnp | grep 9999
使用curl
工具,向该接口发送一个post
请求,例如:
curl -X POST -d "site=juejin,name=pdudo" 127.0.0.1:9999
输出的结果是我们预测的分析http
报文的结果:
总结
systemd
提供了非常多的功能,其中socket
只是其中的一小块,但是确实是非常有意思的,试想一下,服务器上有一个对外的服务,非常重要,但是访问的很少,可能一年也访问不了几次,如果有这样一个工具,当监听到有客户端进行访问的时候,再启动服务,进而响应客户端需求,处理完毕后,再销毁该后端服务,这样既能够解决业务访问需求,又能够避免占用过多的资源,恰恰好。
sshd
就是一个非常好的例子,你可以在虚拟机上安装一个centos 7
,先将sshd
服务先关闭,而后在终端中输入:
systemctl start sshd.socket
恭喜你,你已经学会了systemd socket
了。
作者:pdudo
链接:https://juejin.cn/post/7299731906254241819
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 标签:systemd,socket,配置文件,启动,代码,sys,实例 From: https://www.cnblogs.com/cheyunhua/p/18434171