概要
rubyとpythonのdaemon間でデータの受け渡しをredis(Pub/Sub)1を使って考えていたら、プロセス間通信(POSIX IPC)使ったほうが手軽だよと天の声が聞こえたので試してみました。
なお、今回はraspberry pi上で動かすので、電源問題のためにもプロセス数は可能な限り減らす作戦で検討しています。
POSIX メッセージキューとは
POSIXのシステムコール/ライブラリ関数を通じて利用できる、プロセス間通信の一つ。非同期型通信プロトコルで、誤解を恐れずにいえばRabbitMQなどのメッセージングミドルウェアの超簡易版のようなもの。POSIX準拠なので簡素なAPIでお手軽に利用できます。
詳しくは以下をご参照ください。
システム全体
- OS: Raspbian GNU/Linux 10 (buster)
- ruby: ruby 2.5.7p206 (2019-10-01 revision 67816) [armv7l-linux-eabihf]
- 利用ライブラリ: posix-mqueue
- python: Python 2.7.16
- 利用ライブラリ: posix_ipc
Ruby側の実装(送信側)
どっちが送信/受信でもよいのですが、今回はRubyを送信側としてみました。
利用ライブラリはposix-mqueueを利用させていただきました。
めっちゃシンプル。
require 'posix/mqueue'
require 'json'
def main
m = POSIX::Mqueue.new("/whatever", msgsize: 8192, maxmsg: 10)
10.times do |i|
m.send({ messsage: "hello python world #{i}" }.to_json)
sleep 1
end
m.send ({ message: 'end'}.to_json)
ensure
m.unlink if m
end
if __FILE__ == $PROGRAM_NAME
main
end
Python側の実装(受信側)
こちらも恐ろしくシンプルです。
利用ライブラリはposix_ipc
# -*- coding: utf-8 -*-
import time
import posix_ipc
import json
def main () :
mq = posix_ipc.MessageQueue("/whatever", posix_ipc.O_CREAT)
while True:
data = mq.receive()
j = json.loads(data[0]) # -> (message, priority)のタプルで戻ってくる
print j
if j.get('message') == "end":
break
if __name__ == "__main__" :
main()
結果
想定通りPython側で出力されました!
$ python main.py
{u'messsage': u'hello python world 0'}
{u'messsage': u'hello python world 1'}
{u'messsage': u'hello python world 2'}
{u'messsage': u'hello python world 3'}
{u'messsage': u'hello python world 4'}
{u'messsage': u'hello python world 5'}
{u'messsage': u'hello python world 6'}
{u'messsage': u'hello python world 7'}
{u'messsage': u'hello python world 8'}
{u'messsage': u'hello python world 9'}
{u'message': u'end'}
簡単なソースですが一応githubにも上げておきました。
https://github.com/y-amadatsu/posix-mq-with-raspberry-pi-os
余談
ちなみにPOSIX メッセージを使うとメッセージQueueが /dev/mqueue
配下に表示されます。マウントして ls
や rm
で操作することも可能です。このあたりはすごくUNIX!って感じ
cat /dev/mqueue/whatever
QSIZE:350 NOTIFY:0 SIGNO:0 NOTIFY_PID:0