編集:これはディルドバグであることが判明しました。Python:Numpy.minは組み込み関数を置き換えます:Pyro4エラーの原因:応答シーケンスが同期外れ
編集:私はパイロ(バージョン4.43、Pythonの3.5で働いている分機能に建て
を交換numpy.minによって引き起こされることが判明し、エラーをトラブルシューティング私の進歩のために下へルック.1、Windows 10)、サーバープロセスがワーカーとワーカープロセスが作業を要求して結果を返すのを待つシンプルなクラスタを設定しようとしています。サーバーは結果を受け取ると、それ以上の処理を行います。
現在、私はただ1台のコンピュータで動作させようとしています(ローカルホストを使用し、同じコンピュータからワーカープロセスを起動させる)。
これまでのところ、サーバープロセスを実行することができました。ワーカープロセスはサーバーに接続してデータを要求し、そのデータを処理できましたが、結果を送信しようとするとワーカープロセスがエラーになりますサーバーに送信します。私は奇妙なエラーメッセージに実行しているよ
:徹底検索した後
File "worker.py", line 90, in <module>
main()
File "worker.py", line 87, in main
worker.send_result()
File "worker.py", line 49, in send_result
self.server.recieve(result)
File "C:\Anaconda3\lib\site-packages\Pyro4\core.py", line 171, in __call__
return self.__send(self.__name, args, kwargs)
File "C:\Anaconda3\lib\site-packages\Pyro4\core.py", line 418, in _pyroInvoke
self.__pyroCheckSequence(msg.seq)
File "C:\Anaconda3\lib\site-packages\Pyro4\core.py", line 448, in __pyroCheckSequence
raise errors.ProtocolError(err)
Pyro4.errors.ProtocolError: invoke: reply sequence out of sync, got 0 expected 2
、私は一つだけother person who has had this errorを見つけることができますが、応答が、これは純粋なパイロエラーだったことだった、と彼はパイロを更新するために必要なしかし、私のバージョンは、それが書かれた時の時代をはるかに超えています。
さらに、私の生産コードの外でこのエラーを再現するのに問題があります。私は、単純なバージョンを作成して、エラーがどこから来たのかを特定し、このエラーを取得できませんでした。私は、エラーなしで生産コードで送信された結果の正確な形式の結果をワーカーから送信しました。
ここでは簡単なコードを示していますが、私の設定の構造のアイデアです。 このコードは以下のエラーを再現しません。次のステップでは、過度に複雑化することなくプロダクションコードに近づけることがわかりません。
Serverコード:
#simple_server.py
import Pyro4
import sys, dill
class SimpleServer:
def serve(self):
with open('served data.pkl', 'rb') as f:
data = dill.load(f) #actual data coming from production code
return data
def recieve(self, result):
print(result)
def main():
Pyro4.config.SERIALIZER = 'dill' #default serpent serializer doesn't work
dill.settings['recurse'] = True #dill won't work without this option
server = SimpleServer()
daemon = Pyro4.Daemon()
server_uri = daemon.register(server)
ns = Pyro4.locateNS()
ns.register("test", server_uri)
print('Server running.')
daemon.requestLoop()
if __name__ == '__main__':
main()
ワーカーコード:
#simple_worker.py
import Pyro4
import sys, dill
import numpy as np
import scipy.optimize as opt
class SimpleWorker:
def __init__(self, server):
self.server = server
def recieve_data(self):
self.data = self.server.serve()
def send_result(self):
res = opt.basinhopping(lambda x: sum(x), np.arange(11), niter=2, minimizer_kwargs={'options':{'maxiter':2}})
#This below data structure is the same that I send in production
result = ('ABCD', 'filename.csv', res, 6)
self.server.recieve(result) #creates error in production code but not here
def main():
sys.excepthook = Pyro4.util.excepthook #gives a more meaningful stack trace
Pyro4.config.SERIALIZER = 'dill' #default serpent serializer doesn't work
dill.settings['recurse'] = True #dill won't work without this option
server = Pyro4.Proxy('PYRONAME:test') #connects to pinest server
worker = SimpleWorker(server)
worker.recieve_data()
worker.send_result()
if __name__ == '__main__':
main()
WindowsのCMDコード:
#run_simple_server.bat
set PYRO_SERIALIZERS_ACCEPTED=serpent,json,marshal,pickle,dill
start cmd /C python -m Pyro4.naming
python simple_server.py
pause
#run_simple_worker.bat
python simple_worker.py
pause
注:私は、これらのタイプを送信するために再帰的なオプションとディルを使用する必要がありますのデータ
Pyro4.current_context.seq
をワーカーメイン内に印刷すると、0が返されます。Pyro4.current_context.seq = 2
を試しても、エラーには影響しません。
このエラーを処理する方法を知っている人や、トラブルシューティングを行う際に次に行うべきことは誰でも知りませんか?
編集: Pyro4ソースのレビュー後、このエラーはPyro4のコーディングエラーのために発生しているようです。 core.Daemon.handleRequestでは、メッセージを受信する際にエラーが発生した場合、独自のメッセージシーケンスをゼロに設定し、そのエラーをメッセージとして送信しようとします。しかし、core.Proxy。_pyroInvokeはメッセージを受信しますが、シーケンスがゼロの場合はエラーとして処理する機能はありません。したがって、同期外れエラーの応答シーケンスが発生します。
私は、メッセージの受信エラーを引き起こす根本的な問題を突き止めました。 socketutil.receiveDataは、最小60000を選択する行とメッセージの残りのサイズがmin(60000, size - msglen)
の受信ループを持っています。何らかの形でこれが実行されると、組み込みminではなくnumpy.minが使用され、numpy.minの第2引数が軸番号であるため、エラーが出力されます。これは私のコードでnumpy as np
をインポートしただけで、from numpy import *
をインポートすることも、min関数を直接インポートすることも驚くべきことです。
さらに驚くべきことは、組み込み関数に置き換えることで修正できないということです。私はimport builtins
を試してmin = builtins.min
を試しても、エラーは続く。 inspect.getfile(builtins.min)
を実行すると、Numpyファイルを指します。
min([60000, size - msglen])
の行を切り替えることで問題を完全に回避しようとしましたが、これはnumpyと組み込みのminの両方で機能しますが、min割り当てはサーバーコードに残っていて、そこの関数も混乱します。むしろハック修正として
、私は分関数の上記の変更を保持するだけでなく、私のサーバークラスの初期化時に、私は組み込み関数を保存:
#Store builtin functions as they later get replaced for some unknown reason
b = [t for t in().__class__.__base__.__subclasses__() if t.__name__ == 'Sized'][0].__len__.__globals__['__builtins__']
self.real_builtins = copy.copy(b) #copy so that dict doesn't get updated
そして、サーバが受信するたびかデータを送信すると、まずこの関数を実行します。
def fix_builtins(self):
global builtins
import builtins
__builtins__ = self.real_builtins
#These are all of [i for i in dir(builtins) if i in dir(numpy)]
builtins.abs = __builtins__['abs']
builtins.all = __builtins__['all']
builtins.any = __builtins__['any']
builtins.bool = __builtins__['bool']
builtins.complex = __builtins__['complex']
builtins.float = __builtins__['float']
builtins.int = __builtins__['int']
builtins.max = __builtins__['max']
builtins.min = __builtins__['min']
builtins.object = __builtins__['object']
builtins.round = __builtins__['round']
builtins.str = __builtins__['str']
builtins.sum = __builtins__['sum']
これは現在動作しているようです。しかし、これは明らかに問題を解決する素晴らしい方法ではありません、私はむしろ最初の場所で組み込み関数を置き換えることをやめます...これはパイロ固有の問題ですか?
Pyro自体は確かに組み込み部品を置き換えていません。別のライブラリがこれを行っている場合、すべてのベットはオフです。 それでは、内蔵分をnumpy.minに置き換える原因は何ですか?私がnumpyをインポートすると、何も起きていない。 builtins.minはPythonの独自の組み込み関数です。 –
@Irmenご協力いただきありがとうございます。 Pyroが問題を引き起こしていないのはなぜだと思いますか?私はPyroなしでサーバーコードを完璧に動作させました。Pyroに接続すると問題が発生しました。私が 'numpy import min'を実行したとしても、それは' builtins.min'に取って代わるものではなく、minという名前だけを置き換えるという点で、非常に奇妙な問題です。 –
私はPyroコードベース自体に組み込み関数を混乱させるものは何もないことを意味しました。さて、シリアライザーがやっていることは、私が知らないPyro(pickle、dill)によって呼び出されますが、おそらく彼らはそれを乱しています。これはPyroを使わないとコードがなぜ機能するのかを説明することができます。なぜなら、ピクル/ディルを使ってデータ構造を渡すとは思わないからです。 –