MessagePack - Eine Zusammenfassung des Aufrufs von Python-Methoden (oder umgekehrt) von Ruby mithilfe von RPC.
――Ich verwende hauptsächlich Ruby, aber ich möchte Berechnungsbibliotheken für Wissenschaft und Technologie wie Pythons Numpy und Matplotlib verwenden.
Wenn Sie es für solche Zwecke verwenden, können Sie möglicherweise die guten Punkte jeder Sprache nutzen.
Remote Procedure Call-Protokoll. Serialisieren Sie mit MessagePack.
Einzelheiten zu den technischen Daten finden Sie hier.
Es stehen Bibliotheken zur Verfügung, die MessagePack-RPC in verschiedenen Sprachen implementieren. Die folgenden beiden sind in Ruby und Python implementiert. Implementierungen verschiedener anderer Sprachen wurden veröffentlicht.
Übrigens scheint das Plug-In von neovim mit MessagePack-RPC implementiert werden zu können. Es gibt den Vorteil, dass das Plug-In in verschiedenen Sprachen implementiert werden kann, solange es den MessagePack-RPC-Spezifikationen entspricht.
Die Ruby-Version enthält fast keine Informationen in der README-Datei, selbst wenn Sie sich die offizielle Github-Seite ansehen. Hier finden Sie eine kurze Zusammenfassung der Verwendung.
Zunächst ein Beispiel für eine Ruby-Version eines Servers
server.rb
require 'msgpack/rpc'
class MyHandler
def add(x,y)
return x+y
end
end
svr = MessagePack::RPC::Server.new
svr.listen('localhost', 18800, MyHandler.new)
svr.run
Beispiel für einen Ruby-Client
client.rb
require 'msgpack/rpc'
c = MessagePack::RPC::Client.new('localhost',18800)
result = c.call(:add, 1, 2)
puts result
Führen Sie wie folgt aus.
ruby server.rb & #Server starten
ruby client.rb #Das Ergebnis der Berechnung wird angezeigt
Es kann genauso in Python geschrieben werden.
server.py
import msgpackrpc
class MyHandler(object):
def add(self, x, y):
return x+y
svr = msgpackrpc.Server( MyHandler(), unpack_encoding='utf-8' )
svr.listen( msgpackrpc.Address('localhost',18800) )
svr.start()
client.py
import msgpackrpc
client = msgpackrpc.Client(msgpackrpc.Address("localhost", 18800), unpack_encoding='utf-8')
result = client.call('add', 1, 2)
print( result )
Wenn Sie einen Ruby-Server und einen Python-Client ausführen (oder umgekehrt), erhalten Sie natürlich genau das gleiche Ergebnis.
Im Fall von Python sollte beachtet werden, dass bei der Initialisierung des Clients oder Servers der Standardwert "unpack_encoding =" None "ist und die übertragenen Daten als Byte-Zeichenfolge interpretiert werden. Im obigen Beispiel gibt es kein Problem, da nur numerische Werte gesendet und empfangen werden. Wenn Sie jedoch Zeichenfolgen senden und empfangen möchten, müssen Sie "unpack_encoding =" utf-8 "angeben. Andernfalls sind die empfangenen Daten eine Folge von Bytes, und Sie müssen in Ihrem Programm explizit ".decode (" utf-8 ")" aufrufen. Sofern Sie keine Binärdaten senden möchten, empfiehlt es sich, standardmäßig "unpack_encoding =" utf-8 "anzugeben.
Die Verarbeitung auf der Serverseite ist nicht auf den Fall beschränkt, in dem sie sofort abgeschlossen wird, und es kann Fälle geben, in denen Sie eine Verarbeitung ausführen möchten, die Zeit benötigt. Für solche Fälle ist auch ein asynchroner Ausführungsmechanismus vorgesehen.
Die von call_async
aufgerufene Methode gibt den Prozess sofort zurück und gibt das future
-Objekt zurück.
Das Future-Objekt gibt den Wert zurück, wenn das Ergebnis vom Server zurückgegeben wird, wenn "# get" aufgerufen wird.
Wenn vom Server kein Ergebnis zurückgegeben wird, warten Sie, bis das Ergebnis vom Server erhalten wird.
Der folgende Beispielcode ist schnell zu sehen.
async_client.rb
require 'msgpack/rpc'
c = MessagePack::RPC::Client.new('localhost',18800)
puts "async call"
future1 = c.call_async(:delayed_add, 1, 1, 2)
future2 = c.call_async(:delayed_add, 1, 2, 3)
puts future2.get #Die Reihenfolge muss nicht unbedingt die Reihenfolge des Anrufs sein
puts future1.get
async_server.rb
require 'msgpack/rpc'
class MyHandler
def delayed_add(t,x,y)
puts "delayed_add is called"
as = MessagePack::RPC::AsyncResult.new
Thread.new do
sleep t
as.result(x+y)
end
as
end
end
Sie können das Ergebnis auch mit future.get ()
für Python erhalten.
async_client.py
import msgpackrpc
client = msgpackrpc.Client(msgpackrpc.Address("localhost", 18800), unpack_encoding='utf-8')
future = client.call_async('delayed_add', 3, 1, 2)
print( future.get() )
async_server.py
import msgpackrpc
import threading, time
class MyHandler(object):
def delayed_add(self, t, x, y):
print("delayed_add is called")
ar = msgpackrpc.server.AsyncResult()
def sleep_add():
time.sleep(t)
ar.set_result(x+y)
thread = threading.Thread(target=sleep_add)
thread.start()
return ar
svr = msgpackrpc.Server( MyHandler(), unpack_encoding='utf-8' )
svr.listen( msgpackrpc.Address('localhost',18800) )
svr.start()
Als praktischeres Beispiel sehen Sie hier ein Beispiel für das Zeichnen eines numerischen Ruby-Arrays mit Pythons Matplotlib.
plot_server.py
import msgpackrpc
import matplotlib.pyplot as plt
svr = msgpackrpc.Server( plt, unpack_encoding='utf-8' )
svr.listen( msgpackrpc.Address('localhost',18800) )
svr.start()
plot_client.rb
require 'msgpack/rpc'
c = MessagePack::RPC::Client.new('localhost',18800)
c.timeout = Float::INFINITY
xs = -3.14.step(3.14, 0.1).to_a
ys = xs.map {|x| Math.sin(x) }
c.call( :scatter, xs, ys )
c.call( :show )
Das folgende Fenster wird angezeigt und der Plot wird gezeichnet.
Beachten Sie, dass der Server keine Verarbeitung zurückgibt, bis das gezeichnete Fenster geschlossen wird. Wenn es unverändert bleibt, tritt eine Zeitüberschreitung auf und auf der Clientseite tritt eine Ausnahme auf. Daher wird "c.timeout = Float :: INFINITY" festgelegt.
Hier werden die Ruby- und Python-Prozesse manuell über die Shell gestartet, aber das Starten von Python als externer Prozess im Ruby-Programm scheint eine übersichtliche Oberfläche zu sein, als würde direkt von Ruby aus geplottet. ..
Als Beispiel in die entgegengesetzte Richtung erhalten Sie die Informationen in der Rails-App von Python
rails_server.rb
require 'msgpack/rpc'
require File.join( ENV['RAILS_ROOT'], 'config/environment' )
Object.class_eval do
def to_msgpack(*args) # to_Wenn msgpack nicht definiert ist, zu_Machen Sie es zu einer Spezifikation, nach dem Aufruf von s in msgpack zu konvertieren
to_s.to_msgpack(*args)
end
end
class MyRailsHandler
def get_book(name)
Book.where(name: name).first.as_json
# as_Hash mit json.
end
end
svr = MessagePack::RPC::Server.new
svr.listen('localhost', 18800, MyRailsHandler.new)
svr.run
rails_client.py
import msgpackrpc
client = msgpackrpc.Client(msgpackrpc.Address("localhost", 18800), unpack_encoding='utf-8')
result = client.call('get_book', 'echo') #Ergebnis ist Wörterbuch
print( result )
Der Punkt ist, to_msgpack (* args)
für Object zu definieren.
Für Klassen, für die to_msgpack
nicht definiert ist, wird das hier definierte Objekt # to_msgpack (* args)` aufgerufen. Dies ermöglicht es, Zeitobjekte und dergleichen zu serialisieren.
RPC kann eine einfache Verarbeitung verarbeiten, bei der nur eine Methode aufgerufen wird, wie wir bisher gesehen haben. In den folgenden Fällen scheint RPC es jedoch nicht einfach zu lösen.
array.map {|x| f(x) }
Kann in RPC nicht übergeben werden, wenn Sie ausführen möchtenRecommended Posts