Simulátor distribuované sítě

Užitečné odkazy

Distribuovaná síť

Distribuovaná síť obsahuje několik uzlů a komunikačních kanálů, které propojují jednotlivé uzly. Pomoci komunikačních kanálů mají uzly možnost zasílat a přijímat zprávy.

Distribuovaná síť o třech uzlech může vypadat následovně:

Topologii takové sítě můžeme zapsat stejně jako orientovaný graf:

1
2
3
4
5
TOPOLOGY = [
    [False, True, False],
    [False, False, True],
    [True, False, False]
]

Pokud se podíváme na uzel N1 vidíme, že obsahuje jeden vstupní a jeden výstupní kanál.

Každému uzlu je možné předat zdrojový kód, který má uzel vykonávat. V rámci tohoto zdrojového kódu jsou přístupny všechny komunikační kanály a název daného uzlu. Volitelně by mělo být možné zjistit název procesu z/do kterého komunikační kanál vede.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from multiprocessing import current_process

def function():
    """Function for node"""

    node = current_process()

    for connection in node.out_pipes:
        connection.send("Msg from node {}".format(node.name))

    for connection in node.in_pipes:
        connection.recv()

    time.sleep(2)

V prvním stádiu by měla knihovna fungovat následovně:

1
2
3
4
5
6
7
8
9
10
TOPOLOGY = [[False, True, False],
            [False, False, True],
            [True, False, False]]

# functions - pole funkci pro uzly, delky rovné počtu uzlů

NETWORK = Network(functions, TOPOLOGY)

NETWORK.start()
NETWORK.join()

Třída multiprocessing.Process

Reprezentuje aktivitu, která běží v samostatném procesu. Pro reprezentaci použijeme třídu Process s upravenou funkcionalitou. Více informací naleznete zde.

Třída multiprocessing.Pipe

Komunikační kanál mezi dvěma procesy. Po vytvoření objektu Pipe je vrácena dvojice objektu Connection. První reprezentuje cílový uzel (můžeme pouze přijímat), druhý počáteční (může pouze odesílat). Objekt budeme vytvářet s parametrem duplex=False, ten zařídí, že komunikační kanál je jednosměrný.

1
2
3
4
5
6
7
8
9
10
11
client, server = Pipe(duplex=False)

...

# Odešleme zprávu
client.send("Test")

...

# Příjmeme zprávu na druhém konci
server.recv()

Logování

V rámci multiprocessingu je možné používat globální logger. Použítí je vidět na příkladu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import logging
import multiprocessing

LOGGER = multiprocessing.log_to_stderr()
LOGGER.setLevel(logging.INFO)

def function():
    """Function for node"""

    node = current_process()

    for output_node in node.out_pipes:
        output_node.send("Msg from node {}".format(node.name))

    for input_node in node.in_pipes:
        LOGGER.info("Node: {}, Msg: {}".format(node.name,
                                               input_node.recv()))

    time.sleep(2)

V pozdější verzi by měla knihovna umět logovat do souborů, názvy těchto souborů budou korespondovat s názvy uzlů.

Úkol

Naprogramujte základní verzi knihovny, která umožňí vytvořit distribuovaný systém na základně předané topologie. Knihovnu otestujte na jednoduchém příkladu, kdy každý uzel pošle informační zprávu sousedu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
TOPOLOGY = [[False, True, False],
            [False, False, True],
            [True, False, False]]

Ukázkový log:

[INFO/0] child process calling self.run()
[INFO/MainProcess] process shutting down
[INFO/1] child process calling self.run()
[INFO/1] Node: 1, Msg: Msg from node 0
[INFO/MainProcess] calling join() for process 0
[INFO/2] child process calling self.run()
[INFO/2] Node: 2, Msg: Msg from node 1
[INFO/0] Node: 0, Msg: Msg from node 2
[INFO/1] process shutting down
[INFO/1] process exiting with exitcode 0
[INFO/0] process shutting down
[INFO/2] process shutting down
[INFO/0] process exiting with exitcode 0
[INFO/2] process exiting with exitcode 0
[INFO/MainProcess] calling join() for process 1
[INFO/MainProcess] calling join() for process 2