systemdのsocketを使ってserverを作る
論よりコード
TCP localhost:18080 で待ち受けて、 implementation.bash
を実行した結果を journal に書き込む例です。
※ systemd は一般ユーザ権限でも利用可能です。今回は一般ユーザ権限で行いました。
その際、systemd 関連のファイルは $HOME/.config/systemd/user/
に配置する必要があります。ない場合は mkdir -p $HOME/.config/systemd/user/
として作ってからファイルを配置してください。
→ 実は systemctl --user enable $PWD/server.socket
と、パスを指定すれば自動的にディレクトリを作ってくれてsymlinkでファイルを配置してくれることがわかりました。
server.socket
ファイル
server.socket
ファイルはSocket を作成して待ち受けをし、クライアントから接続を受け付けたら [email protected]
を呼び出します。 (man systemd.socket)
[email protected]
というファイル名は Service =
で指定可能です。指定しない場合は .socket
のファイル名から導出されます(今回の例だと server
)。
このファイルは systemctl --user enable
コマンドで自動的に $HOME/.config/systemd/user/
に配置されます。
[Unit]
Description = Simple TCP server (listener)
[Socket]
ListenStream = 127.0.0.1:18080
Accept = yes
[Install]
WantedBy=sockets.target
[email protected]
ファイル
ファイル名の @
はテンプレートユニットであることの宣言となります。 **.socket
からの呼び出し毎に fork するような形で実体が作られて実行されます。
このファイルは systemctl --user enable
コマンドで自動的に $HOME/.config/systemd/user/
に配置されます。
[Unit]
Description = Simple TCP server (implementation)
[Service]
Environment = LANG=C
ExecStart = /bin/bash /home/USER/implementation.bash
StandardInput = socket
StandardOutput = journal
StandardError = journal
[Install]
WantedBy = server.socket
[Install]
の WantedBy =
は、呼び出し元である server.socket
を指定します。
StandardInput
は、 implementation.bash
への標準入力のソースを指定しています。
socket となっていれば **.socket
で受け付けた接続の入力を STDIN として渡すことができます。
StandardOutput
および StandardError
は implementation.bash
の出力をどこに返すかという指定です。
socket となっていれば **.socket
で受け付けた接続に返します。 journal となっていれば journalctl
で見ることができます。
その他出力先の指定は systemd.exec をご覧ください。複数同時は指定できないようです。
実装部
今回は bash で実装しました。socketからの入力は **.service
の StandardInput
を経由して標準入力に渡されますので read
で読み込むことができます。
このファイルは [email protected]
の ExecStart
で指定されています。
#!/bin/bash
read INPUT
echo "Your input is $INPUT"
echo -n "Today is "
date
exit 0
systemd へ登録する
3つのファイルの配置が終わったら systemctl
を使って systemd に登録します。start するのは .socket のみです。
$ systemctl --user enable $PWD/[email protected]
$ systemctl --user enable $PWD/server.socket
$ systemctl --user start server.socket
journalctl で確認してみます。
$ journalctl -n 1
-- Logs begin at Thu 2019-11-07 21:17:45 JST, end at Fri 2019-11-08 00:00:30 JST. --
Nov 08 00:00:30 raspberrypi systemd[506]: Listening on Simple TCP server (listener).
テスト
$ echo "hello!" | nc 127.0.0.1 18080
$ journalctl -n 4
-- Logs begin at Thu 2019-11-07 21:17:45 JST, end at Fri 2019-11-08 00:01:39 JST. --
Nov 08 00:01:39 raspberrypi systemd[506]: Started Simple TCP server (implementation) (127.0.0.1:50772).
Nov 08 00:01:39 raspberrypi bash[8284]: Your input is hello!
Nov 08 00:01:39 raspberrypi bash[8284]: Today is Fri Nov 8 00:01:39 JST 2019
Nov 08 00:01:39 raspberrypi systemd[506]: [email protected]:18080-127.0.0.1:50772.service: Succeeded.
[email protected]
や実装部の変更の反映
[email protected]
は server.socket
からの新規接続のタイミングで都度読み込まれます。そのため [email protected]
の変更は次回呼び出しから即適用されます。実装部も同様です。
ここから先はどうでもいい話です。
サーバの実装方法について
TCP/UDPやUnix Domain Socketといった待ち受けをするサーバを構築する方法は、概ね以下の通りです。
-
listen(2) を利用
- 待ち受け部、実装部を包括して実現する自己完結モデル
-
inetd(8) を利用
- 待ち受け部を inetd が担当し、実装部は STDIN/STDOUT を介して実行されるプログラムとなる分業モデル
今回の systemd によるサーバの実現は inetd(8) のモダン実装ということになります。
listen(2) を利用する場合
たとえば Ruby で書く場合はこんな感じです。極力 implementation.bash
と合わせてみました。
require "socket"
server = TCPServer.open("127.0.0.1", 18080)
while true
socket = server.accept
Thread.start(socket) do |s|
while buffer = socket.gets
socket.puts "Your input is #{buffer}"
socket.puts "Today is #{Time.now}"
end
socket.close
end
end
server.close
考察
listen(2) を利用する場合は何から何まで自力実装が可能であるため、細かい実装やケイパビリティ、速度面でのメリットが得られやすい。その代わり「実装しなければならない」という問題もある。
今回の systemd.socket を利用する場合は、例えば MaxConnectionsPerSource といった設定があるため、それを利用するだけで良いが、どの程度受けられるかは未知数であるため、要検証。
どのような時に必要となるの?
いわゆるプロセス間通信を容易に実現する方法ということになります。これにより、「最初は定時起動する要件だけだったけど、他からの割込みで起動する要件を追加したい」に対しても容易に増やすことできます。まさにマイクロサービス的なことが可能となるわけです。
あとがき
ファイルが多いのが難点だけど、慣れると結構便利。
一番のポイントは、全部ファイル名を同じにして、拡張子だけが異なる状態が管理しやすい。
さて、、、仕事するか。
EoT
Author And Source
この問題について(systemdのsocketを使ってserverを作る), 我々は、より多くの情報をここで見つけました https://qiita.com/ma2shita/items/f366320cc504c04b6b6a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .