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 Network obsahuje následující metody:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Network():
"""Represents distributed network"""
def __init__(self, functions, topology):
pass
def start(self):
"""Starts all nodes in the network"""
pass
def terminate(self):
"""Terminate all nodes in the network"""
pass
def kill(self):
"""Kill all nodes in the network"""
pass
def join(self):
"""Join all nodes in the network"""
pass
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.
1
2
3
4
5
6
7
from multiprocessing import Process
class Node(Process):
def __init__(self, name, function):
Process.__init__(self, name=name, target=function)
...
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ý. Více informací naleznete zde.
1
2
3
4
5
6
7
8
9
10
11
reciever, sender = Pipe(duplex=False)
...
# Odešleme zprávu
sender.send("Test")
...
# Příjmeme zprávu na druhém konci
reciever.recv()
Třída Node obsahuje následující metody:
1
2
3
4
5
6
7
8
9
10
11
12
class Node(Process):
"""Represents one node in the distributed network"""
def __init__(self, name, function):
Process.__init__(self, name=name, target=function)
pass
def add_in_pipe(self, pipe):
pass
def add_out_pipe(self, pipe):
pass
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ů. Více informací o logování v knihovně multiprocessing naleznete zde
Ú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
TOPOLOGY = [[False, True, False],
[False, False, True],
[True, False, False]]
Ukázkový log:
[INFO/0] child process calling self.run()
[INFO/1] child process calling self.run()
[INFO/0] Node: 0, Msg: Msg from node 1
[INFO/2] child process calling self.run()
[INFO/2] Node: 2, Msg: Msg from node 0
[INFO/1] Node: 1, Msg: Msg from node 2
[INFO/0] process shutting down
[INFO/0] process exiting with exitcode 0
[INFO/1] process shutting down
[INFO/2] process shutting down
[INFO/1] process exiting with exitcode 0
[INFO/2] process exiting with exitcode 0
[INFO/MainProcess] process shutting down