1

I am running an application which cannot sit and wait the successful/unsuccessful connection to a Python Manager. The client application should try to send some info to the supposedly running server, and in case it fails, another measure is taken. The problem is that whenever the server is down the connection takes a lot of time to return the control to the client application, and it cannot waste time waiting for it because there is other stuff to do.

I came up with a scheme where an intermediary object is in charge of the connection but it only works once. Let's say that for the first time, when there is still no connection to the server, this intermediary object handles the connecting part without blocking the client application. If, for some reason, the server goes down and comes back again, I can't get it to work anymore.

Suppose I have the following server:

# server.py

from multiprocessing import Queue, managers
from multiprocessing.queues import Empty
import select
import threading


class RServer(object):
    def __init__(self, items_buffer):

        self.items_buffer = items_buffer

    def receive_items(self):
        while True:
            (_, [], []) = select.select([self.items_buffer._reader], [], [])
            while True:
                try:
                    item = self.items_buffer.get(block=False)
                    # do something with item
                    print('item received')
                except Empty:
                    break


class SharedObjectsManager(managers.BaseManager):
    pass


if __name__ == '__main__':

    items_buffer = Queue()

    remote_server = RServer(items_buffer)

    remote_server_th = threading.Thread(target=remote_server.receive_items)
    remote_server_th.start()

    SharedObjectsManager.register('items_buffer', callable=lambda: items_buffer)

    shared_objects_manager = SharedObjectsManager(address=('localhost', 5001),
                                                  authkey=str.encode('my_server'),
                                                  serializer='xmlrpclib')

    s = shared_objects_manager.get_server()
    s.serve_forever()

And here is the intermediary object to handle the connection:

# bridge.py

from multiprocessing.managers import BaseManager
import threading
import socket


class ConnectionManager():
    def __init__(self):
        self.remote_manager = BaseManager(address=('localhost', 5001),
                                          authkey=b'my_server',
                                          serializer='xmlrpclib')
        self.remote_manager.register('items_buffer')
        self.items_buffer = None
        self.items_buffer_lock = threading.Lock()
        self.connecting = False
        self.connecting_lock = threading.Lock()
        self.connection_started_condition = threading.Condition()

    def transmit_item(self, item):
        try:
            with self.items_buffer_lock:
                self.items_buffer.put(item)
        except (AttributeError, EOFError, IOError):
            with self.connection_started_condition:
                with self.connecting_lock:
                    if not self.connecting:
                        self.connecting = True

                        connect_th = threading.Thread(target=self.connect_to_server,
                                                      name='Client Connect')
                        connect_th.start()

                self.connection_started_condition.notify()

            raise ConnectionError('Connection Error')

    def connect_to_server(self):
        with self.connection_started_condition:
            self.connection_started_condition.wait()
        try:
            self.remote_manager.connect()
        except socket.error:
            pass
        else:
            try:
                with self.items_buffer_lock:
                    self.items_buffer = self.remote_manager.items_buffer()
            except (AssertionError, socket.error):
                pass

        with self.connecting_lock:
            self.connecting = False


class ConnectionError(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)

And finally the client application:

# client.py

import time

from bridge import ConnectionManager, ConnectionError


remote_buffer = ConnectionManager()

while True:
    try:
        remote_buffer.transmit_item({'rubish': None})
        print('item sent')
    except ConnectionError:
        # do something else
        print('item not sent')

    # do other stuff
    print('doing other stuff')
    time.sleep(15)

I am for sure doing something wrong with the thread but I can't figure it out. Any idea?

luke_16
  • 187
  • 1
  • 1
  • 11

0 Answers0