Освобождение ресурсов, когда клиент Pyro4 неожиданно отключается

У меня есть распределенная система Pyro4 с несколькими клиентами, подключающимися к одному серверу. Эти клиенты подключаются к удаленному объекту, и этот объект может выделять некоторые ресурсы в системе (виртуальные устройства в моем случае).

Как только клиент отключается (скажем, из-за сбоя), мне нужно освободить эти ресурсы. Как правильно определить, что конкретный клиент отключился от определенного объекта?

Я пробовал разные вещи:

  • Переопределение Daemon.clientDisconnected метод. Я получаю connection параметр из этого метода. Но я не могу соотнести это с объектом, потому что у меня нет доступа к тому удаленному объекту, к которому относится это соединение.
  • С помощью Pyro4.current_context в Daemon.clientDisconnected, Это не работает, потому что это локальный объект потока. Если у меня подключено больше клиентов, чем потоков в моем пуле, я получаю повторяющиеся контексты.
  • С помощью Proxy._pyroAnnotations как в примере "usersession", доступном в проекте Pyro4, мне не помогает, потому что я снова получаю аннотацию от Pyro4.core.current_context.annotations атрибут, который показывает мне неправильные аннотации, когда Daemon.clientDisconnected называется (я думаю, из-за проблем, связанных с потоком).
  • С помощью instance_mode="session" и __del__ метод в удаленном классе (так как каждый клиент будет иметь отдельный экземпляр класса, поэтому предполагается, что экземпляр будет уничтожен после отключения клиента). Но это зависит от __del__ метод, который имеет некоторые проблемы, как отметили бы некоторые программисты Python.

Я добавил свое текущее решение в качестве ответа, но я действительно хотел бы знать, есть ли более элегантный способ сделать это с Pyro4, поскольку этот сценарий является повторяющимся паттерном в сетевом программировании.

2 ответа

Решение

Pyro 4.63 will probably have some built-in support for this to make it easier to do. You can read about it here http://pyro4.readthedocs.io/en/latest/tipstricks.html and try it out if you clone the current master from Github. Maybe you can take a look and see if that would make your use case simpler?

Я использую Proxy._pyroHandshake атрибут в качестве идентификатора клиента на стороне клиента и переопределить Daemon.validateHandshake а также Daemon.clientDisconnected, Таким образом, при каждом новом соединении я сопоставляю данные рукопожатия (уникальные для каждого клиента) соединению. Но я действительно хотел знать, есть ли элегантный способ сделать это в Pyro4, который часто встречается в сетевом программировании.

Обратите внимание, что вместо использования прокси-сервера в качестве атрибута клиента, клиент также может расширять Pyro4.Proxy и использовать _pyroAnnotations для отправки идентификатора клиента на все удаленные вызовы.

class Client:

    def __init__(self):

        self._client_id = uuid.uuid4()
        self._proxy = Pyro4.Proxy("PYRO:server@127.0.0.1")
        self._proxy._pyroHandshake = self._client_id
        self._proxy._pyroBind()

    def allocate_resource(self, resource_name):
        self._proxy.allocate_resource(self._client_id, resource_name)


class Server:

    def __init__(self):

        self._client_id_by_connection = {}
        self._resources_by_client_id = {}

    def client_connected(self, connection, client_id):

        self._client_id_by_connection[client_id] = connection
        self._resources_by_client_id[client_id] = []

    def client_disconnected(self, connection):

        client_id = self._client_id_by_connection[connection]

        for resource in self._resources_by_client_id[client_id]
            resource.free()

    @Pyro4.expose
    def allocate_resource(self, client_id, resource_name)

        new_resource = Resource(resource_name)
        self._resources_by_client_id[client_id].append(new_resource)

server = Server()
daemon.register(server, objectId="server")
daemon.clientDisconnect = server.client_disconnected
daemon.validateHandshake = server.client_connected
daemon.requestLoop()
Другие вопросы по тегам