web-dev-qa-db-fra.com

Python + ZMQ: l'opération ne peut pas être accomplie dans l'état actuel

J'essaie de faire communiquer un programme python avec un autre programme python via zeromq en utilisant le modèle demande-réponse. Le programme client doit envoyer une demande au programme serveur qui répond.

J'ai deux serveurs tels que lorsqu'un serveur tombe en panne, l'autre prend le relais. La communication fonctionne parfaitement lorsque le premier serveur fonctionne, cependant, lorsque le premier serveur tombe en panne et lorsque je fais une demande au second serveur, je vois l'erreur:

zmp.error.ZMQError: l'opération ne peut pas être accomplie dans l'état actuel

Code du serveur 1:

# Run the server
while True:

    # Define the socket using the "Context"
    sock = context.socket(zmq.REP)
    sock.bind("tcp://127.0.0.1:5677")
    data = sock.recv().decode("utf-8")
    res = "Recvd"
    sock.send(res.encode('utf-8'))

Code du serveur 2:

# Run the server
while True:

    # Define the socket using the "Context"
    sock = context.socket(zmq.REP)
    sock.bind("tcp://127.0.0.1:5877")
    data = sock.recv().decode("utf-8")
    res = "Recvd"
    sock.send(res.encode('utf-8'))

Code de client:

# ZeroMQ Context For distributed Message amogst processes
context = zmq.Context()
sock_1 = context.socket(zmq.REQ)
sock_2 = context.socket(zmq.REQ)
sock_1.connect("tcp://127.0.0.1:5677")
sock_2.connect("tcp://127.0.0.1:5877")

try:
    sock_1.send(data.encode('utf-8'), zmq.NOBLOCK)
    socks_1.setsockopt(zmq.RCVTIMEO, 1000)
    socks_1.setsockopt(zmq.LINGER, 0)
    data = socks_1.recv().decode('utf-8') #receive data from the main node  

except:
    try:
        #when server one fails
        sock_2.send(data.encode('utf-8'), zmq.NOBLOCK)
        socks_2.setsockopt(zmq.RCVTIMEO, 1000)
        socks_2.setsockopt(zmq.LINGER, 0)
        data = socks_2.recv().decode('utf-8')
    except Exception as e:
         print(str(e))

Quel est le problème avec cette approche? Comment puis-je résoudre ce problème?

8
QuikProBroNa

Q: Comment puis-je résoudre ce problème?
R: Évitez le risque connu de blocage REQ/REP!

Bien que le ZeroMQ soit un cadre puissant, il est nécessaire de comprendre sa composition interne pour la robuste et fiable conception et prototypage de systèmes distribués.

Après un examen plus approfondi, l’utilisation d’un modèle REQ/REP formel de communication formel peut laisser (et laisse) des tiers dans une impasse réciproque: où l’un attend l’autre de faire une étape, ce qui ne sera jamais accompli, et moyen d'échapper à l'impasse.

Pour plus de détails illustrés et schéma FSA, voir cet article

Next, un système de basculement doit survivre à toute collision entre ses propres composants. Ainsi, il faut bien concevoir la signalisation d’état du système distribué et éviter autant que possible les dépendances sur la conception/le pas à pas/le blocage élément-FSA, sans quoi le comportement à sécurité intrinsèque n’est qu’une illusion.

Always manipulez les ressources avec précaution, ne considérez pas les composants de la signalisation/messagerie intelligente ZeroMQ comme des types de "consommables jetables". Cela pourrait être toléré dans les exemples classiques, pas dans les environnements de système de production. Vous devez toujours payer les coûts (temps, allocations de ressources/désaffectations/ramassage des ordures). Comme indiqué dans les commentaires, ne laissez jamais la création/affectation de ressources sans contrôle. while True: .socket(); .bind(); .send(); est brutalement erroné et détériore le reste de la conception.

4
user3666197

Implémentez le modèle de pirate lazy . Créez un new socket à partir de votre contexte lorsqu'une erreur est interceptée, avant de tenter à nouveau d'envoyer le message.

La bonne solution en force brute consiste à fermer et à rouvrir le socket REQ Après une erreur

Ici est un exemple en python.

#
#   Author: Daniel Lundin <dln(at)eintr(dot)org>
#
from __future__ import print_function

import zmq

REQUEST_TIMEOUT = 2500
REQUEST_RETRIES = 3
SERVER_ENDPOINT = "tcp://localhost:5555"

context = zmq.Context(1)

print("I: Connecting to server…")
client = context.socket(zmq.REQ)
client.connect(SERVER_ENDPOINT)

poll = zmq.Poller()
poll.register(client, zmq.POLLIN)

sequence = 0
retries_left = REQUEST_RETRIES
while retries_left:
    sequence += 1
    request = str(sequence).encode()
    print("I: Sending (%s)" % request)
    client.send(request)

    expect_reply = True
    while expect_reply:
        socks = dict(poll.poll(REQUEST_TIMEOUT))
        if socks.get(client) == zmq.POLLIN:
            reply = client.recv()
            if not reply:
                break
            if int(reply) == sequence:
                print("I: Server replied OK (%s)" % reply)
                retries_left = REQUEST_RETRIES
                expect_reply = False
            else:
                print("E: Malformed reply from server: %s" % reply)

        else:
            print("W: No response from server, retrying…")
            # Socket is confused. Close and remove it.
            client.setsockopt(zmq.LINGER, 0)
            client.close()
            poll.unregister(client)
            retries_left -= 1
            if retries_left == 0:
                print("E: Server seems to be offline, abandoning")
                break
            print("I: Reconnecting and resending (%s)" % request)
            # Create new connection
            client = context.socket(zmq.REQ)
            client.connect(SERVER_ENDPOINT)
            poll.register(client, zmq.POLLIN)
            client.send(request)

context.term()
0
Vassilis