決定する必要があるのは、少なくとも 2 つの問題です。
- メッセージを交換するには?
- どの形式で?
1について。TCPソケットは最低レベルであり、メッセージ境界の認識などの低レベルの処理を行う必要があります。また、TCP 接続は信頼性の高い配信を提供しますが、接続がリセットされない限り (たとえば、一時的なネットワーク障害が原因で) のみです。TCP 接続がリセットされたときにアプリケーションを正常に回復させたい場合は、新しい接続を介して再送信する必要があるものを追跡するために、何らかの形式のメッセージ確認応答を実装する必要があります。OMQ は、プレーンな TCP 接続よりも高いレベルの抽象化を提供します。バイト ストリームを処理する必要はありませんが、メッセージ全体を処理する必要があります。それでも信頼性の高い配信は得られず、メッセージが失われる可能性がありますが、信頼性の高い配信を保証するために使用できるいくつかの通信パターンが提供されます。0MQ もパフォーマンスが高く、IMO は良い選択です。
2 に関しては、他の言語との相互運用性が必要ない場合、Pickle は非常に便利で Pythonic の選択肢です。相互運用性が必要な場合は、JSON を検討できます。パフォーマンスが問題になる場合は、Google プロトコル バッファなどのバイナリ形式を検討できます。この最後の選択は、ほとんどの作業を必要とします (.idl ファイルでメッセージ形式を定義する必要があります)。
プレーンなソケットを介したメッセージ (シリアライズ可能な任意の Python オブジェクト) の交換がどのように見えるかを見てみましょう:
def send(sockfd, message):
string_message = cPickle.dumps(message)
write_int(sockfd, len(string_message))
write(sockfd, string_message)
def write_int(sockfd, integer):
integer_buf = struct.pack('>i', integer)
write(sockfd, integer_buf)
def write(sockfd, data):
data_len = len(data)
offset = 0
while offset != data_len:
offset += sockfd.send(data[offset:])
悪くはありませんが、ご覧のとおり、メッセージの長さのシリアル化を処理する必要があるのは非常に低レベルです。
そして、そのようなメッセージを受け取るには:
def receive(self):
message_size = read_int(self.sockfd)
if message_size == None:
return None
data = read(self.sockfd, message_size)
if data == None:
return None
message = cPickle.loads(data)
return message
def read_int(sockfd):
int_size = struct.calcsize('>i')
intbuf = read(sockfd, int_size)
if intbuf == None:
return None
return struct.unpack('>i', intbuf)[0]
def read(sockfd, size):
data = ""
while len(data) != size:
newdata = sockfd.recv(size - len(data))
if len(newdata) == 0:
return None
data = data + newdata
return data
しかし、これはエラーを適切に処理しません (どのメッセージが正常に配信されたかを判断しようとしません)。