Remote procedure call (RPC)

Užitečné odkazy

Kolekce namedtuple

Pro vytvoření jednoduché třídy na reprezentaci zprávy je vhodné použít namedtuple. Interně se jedná o tuple a lze s ním tak pracovat. Pozor nejedná se o mutovatelnou strukturu.

1
2
3
4
5
from collections import namedtuple

Message = namedtuple('Message', ['type', 'value'])

new_message = Message(type='Error', value='Invalid function name.')

Informace o funkci

V dnešním úkolu bude užitečné zjistit název funkce a počet a jména argumentů funkce.

1
2
3
4
5
6
7
8
def function(a, b):
    return a + b

# name (string)
function.__name__

# list of arguments names (strings)
function.__code__.co_varnames 

RPC Server

Úkolem bude vytvořit RPC Server, který půjde spustit jako vlákno v rámci kódu třídy Node. Uživatel při vytvoření RPC Serveru předá seznam funkcí, které bude RPC podporovat. Tyto funkce poté mohou klienti serveru vzdáleně volat s dodanýmy argumenty.

V aktuální verzi bude RPC Server obsluhovat veškeré zprávy, které do daného objektu Node přijdou. Zprávy které tak nepatří RPC Serveru může zahazovat. Při vytváření RPC Serveru je tedy nutné, aby byl předán objekt Node a logger pro logování do souboru daného uzlu.

Jakmile RPC Server obdrží zprávu pro vzdálený výpočet, vytvoří vlákno s vybranou funkcí a argumenty. Vlákno po skončení výpočtu samo pošle výsledek uzlu, který si o výpočet zažádal.

RPC Server je možné ukončit zavoláním metody stop() (to klasické vlákno neumožňuje).

Vzorová šablona třídy RPCServer

1
2
3
4
5
6
7
class RPCServer(threading.Thread):
    def __init__(self, functions, node, logger):
        pass
        threading.Thread.__init__(self)

    def run(self):
        pass

Kód uzlu master může vypadat nasledovně:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def master():
    # get current node object
    node = current_process()

    # get logger
    logger = node.get_logger()

    logger.info(f"Starting node.")

    def add(x, a):
        return x + a

    def sub(x, a):
        return x - a

    rpc_server = RPCServer([add, sub], node, logger)

    rpc_server.start()
    rpc_server.join()

    logger.info(f"Shutting down.")

Jednoduchý klient, který zažádá o jeden výpočet, a po obdržení výsledku skončí:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def client():
    # get current node object
    node = current_process()

    # get logger
    logger = node.get_logger()

    message = RPCMessage('add', [1, 2], None)
    logger.info(f"{message}")
    node.send_to('master', message)

    result = node.recv_from('master')
    logger.info(f"{result}")

    logger.info(f"Starting node.")

    logger.info(f"Shutting down.")

Úkol
Naprogramujte RPC Server jako potomka třídy threading.Thread dle specifikací výše.