Differences between revisions 7 and 8
Revision 7 as of 2009-05-24 17:03:03
Size: 7633
Editor: lukisi
Comment:
Revision 8 as of 2009-05-25 08:16:29
Size: 9365
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 54: Line 54:
Attenzione ora. Io so che devo chiedere informazioni o servizi al nodo 1.2.12.34, oppure al nodo partecipante più "vicino" (per vicino si intende qui in senso di numeri, concetto molto astratto). So che il gnodo di livello 1 1.2.5 contiene alcuni nodi partecipanti, ma non posso sapere quale di questi sia il più "vicino" al 1.2.12.34. Attenzione ora. Io so che devo chiedere informazioni o servizi al nodo 1.2.12.34, oppure al nodo partecipante più "vicino" (per vicino si intende qui in senso di numeri, concetto molto astratto). So che il gnodo di livello 1 e indirizzo 1.2.5 contiene alcuni nodi partecipanti, ma non posso sapere quale di questi sia il più "vicino" al 1.2.12.34.
Line 60: Line 60:
Il metodo {{{msg_send}}} ha come argomenti il mittente del messaggio ({{{sender_nip=[4,3,2,1]}}}) il destinatario "perfetto" del messaggio ({{{hip=[34,12,2,1]}}}) e il messaggio ({{{msg}}}) che è una tupla {{{(func_name, args)}}}. Il metodo '''{{{msg_send}}}''' ha come argomenti il mittente del messaggio ({{{sender_nip=[4,3,2,1]}}}) il destinatario "perfetto" del messaggio ({{{hip=[34,12,2,1]}}}) e il messaggio ({{{msg}}}) che è una tupla {{{(func_name, args)}}}.
Line 64: Line 64:
Poi usa il metodo {{{neigh_get}}}. Questo, a partire da un {{{hip}}} (che potrebbe avere i livelli più bassi a {{{None}}}) restituisce l'oggetto {{{Neigh}}} del vicino da usare come gateway. Trovato questo vicino richiama su di lui lo stesso metodo {{{msg_send}}}, che è remotable, passando {{{sender_nip}}}, il nuovo {{{hip}}} e il messaggio {{{msg}}}. '''''TODO''': l'hip passato al metodo remoto è quello con dentro i possibili None. Questo non causa problemi la prossima volta che si esegue H? Forse andrebbe passato l'hip originale?'' Poi usa il metodo '''{{{neigh_get}}}'''. Questo, a partire da un {{{hip}}} (che potrebbe avere i livelli più bassi a {{{None}}}) restituisce l'oggetto {{{Neigh}}} del vicino da usare come gateway. Trovato questo vicino richiama su di lui lo stesso metodo {{{msg_send}}}, '''che è remotable''', passando {{{sender_nip}}}, il nuovo {{{hip}}} e il messaggio {{{msg}}}. '''''TODO''': l'hip passato al metodo remoto è quello con dentro i possibili None. Questo non causa problemi la prossima volta che si esegue H? Forse andrebbe passato l'hip originale?''
<<BR>>
Dopo alcuni passi il metodo {{{msg_send}}} verrà richiamato proprio sull'host destinatario (quello perfetto o il più vicino partecipante). Dopo aver richiamato il metodo {{{H}}} il nodo si accorge di essere lui stesso il destinatario e che quindi deve eseguire l'azione richiesta.

Per eseguire l'azione richiesta richiama il metodo '''{{{msg_exec}}}'''. Questo usa il messaggio (la tupla func_name e args) con il suo metodo {{{dispatch}}}, essendo la classe {{{P2P}}} una derivata di {{{RPCDispatcher}}}. In questo modo viene eseguito il metodo remotable indicato nella chiamata del servizio.
<<BR>>
Il valore restituito dal metodo remotable indicato nella chiamata al servizio viene a sua volta restituito dal metodo {{{msg_exec}}}; poi viene a sua volta restituito dal metodo remotable {{{msg_send}}} chiamato nell'ultimo passo dell'instradamento; viene poi restituito a ritroso dallo stesso metodo chiamato nei vari passi dell'instradamento, fino ad essere restituito dal metodo {{{msg_send}}} chiamato in modo locale sul nodo mittente. '''''TODO''': Domanda: non sarebbe più performante e meno pesante per l'intera rete se la risposta avvenisse in modo diretto dal destinatario al chiamante con una connessione diretta visto che ora il destinatario ha l'IP preciso del mittente nell'argomento "sender_nip"?''

La chiamata a {{{msg_send}}} non viene fatta direttamente, si vuole fare uso del meccanismo del {{{FakeRmt}}} (vedi il [[../ModuloRPC|modulo RPC]]). Si usa quindi il metodo '''{{{peer}}}''' per ottenere una istanza di {{{FakeRmt}}} che sia a conoscenza delle informazioni da passare al metodo {{{msg_send}}}.
<<BR>>
Il metodo {{{peer}}} si può richiamare specificando l'argomento keyword {{{key}}} oppure direttamente l'argomento keyword {{{hip}}}. Di norma si usa specificando la chiave da cui calcolarsi l'hip a cui rivolgersi. '''''TODO''': anche in questo caso a partire da key si eseguono sia il metodo h sia il metodo H. Invece dovrebbe essere eseguito solo h per avere l'hip perfetto da passare al metodo msg_send. Giusto?''
<<BR>>
Ad esempio il servizio {{{Coordinator}}} si usa così:
<<BR>>
{{{ co2 = self.coordnode.peer(key = (lvl+1, newnip))}}}
<<BR>>
{{{ newnip = co2.going_in(lvl)}}}
<<BR>>
{{{ co2.close()}}}

Line 72: Line 92:
Il metodo {{{peer(hip,key)}}} ritorna una istanza di {{{FakeRmt}}} (vedi il [[../ModuloRPC|modulo RPC]]) quindi si può usare la sintassi astratta per eseguire il metodo {{{rmt}}}; questo viene overridato per chiamare il metodo {{{msg_send}}};

Il metodo {{{peer}}} si può richiamare con uno o l'altro degli identificatori del peer. Ad esempio il servizio {{{Coordinator}}} (vedi il [[../ModuloCoord|modulo coord]]) si usa così:
<<BR>>
{{{ co2 = self.coordnode.peer(key = (lvl+1, newnip))}}}
<<BR>>
{{{ newnip = co2.going_in(lvl)}}}
<<BR>>
{{{ co2.close()}}}

Il modulo p2p

La classe PartecipantNode rappresenta un potenziale nodo partecipante. Ha l'attributo partecipant che dice se effettivamente lo è (in un dato servizio P2P, cioè in una data MapP2P)

La classe MapP2P eredita dalla classe Map e ha come dataclass la PartecipantNode.
Il suo attributo pid viene specificato al momento della creazione, nel costruttore. E' l'ID del servizio P2P che questa mappa rappresenta.
Il metodo partecipate si segna che il nodo partecipa. Memorizza se stesso come partecipante all'interno della mappa.
Il metodo node_del viene overridato dalla classe base Map solo per rendere tale metodo una microfunc. Sarà dichiarato gestore dell'evento NODE_DELETED della Maproute del nodo.
Il metodo me_changed sostituisce il me_change della classe base Map. Rende tale metodo una microfunc con dispatcher. Ha un nome diverso solo per accettare anche un altro parametro, il old_me, che non userà; questo per poterlo dichiarare come gestore dell'evento ME_CHANGED della Maproute del nodo.

La classe P2P

La classe P2P è la base dalla quale erediteranno tutte le classi che implementano un servizio p2p.

Eredita a sua volta dalla classe RPCDispatcher (vedi il modulo RPC). Viene istanziata passando le istanze di Radar, Maproute, e il PID (P2p ID) del servizio.
Nel suo costruttore si istanzia la MapP2P con le stesse dimensioni della Maproute e con il mio stesso NIP. Inoltre registra dei gestori (i metodi node_del e me_changed) per gli eventi ME_CHANGED e NODE_DELETED della Maproute. In questo modo si incarica di tenere sincronizzate queste mappe.
Ha un attributo partecipant che valorizza inizialmente a False.
Dichiara metodi remotable il partecipant_add e il msg_send.
Essendo una derivata della classe RPCDispatcher richiama il suo costruttore indicando che la sua root_instance è se stessa (self).

Il metodo h serve per ricavare da una generica chiave un indirizzo NIP.
Nella classe base P2P la chiave è lo stesso NIP che viene restituito. Si tratta di una implementazione ovviamente poco utile.
Il metodo deve essere quindi ridefinito nel modulo che implementerà un certo servizio, con un algoritmo che partendo da una chiave significativa per quel servizio, restituisca un NIP.
Per esempio nel modulo ANDNA la chiave sarà la stringa hostname, per poter poi trovare il nip del register node di quell'hostname; nel modulo Coord la chiave sarà una tupla (lvl, ip), per poter trovare il nip del coordinator node per il gnodo di livello lvl di quel ip; e così via.

Il NIP ottenuto con il metodo h potrebbe indicare un nodo non presente o che non partecipa a questo servizio P2P.
A questo punto va quindi utilizzato il metodo H per ottenere un indirizzo NIP valido.
Vediamo come funziona il metodo H.
Supponiamo che il nostro nodo abbia IP 1.2.3.4 (self.mapp2p.me = [4,3,2,1]) e che vogliamo trovare un IP valido per il IP 1.2.12.34 (IP = [34,12,2,1])
Partendo dal livello l più alto cerco nella mia mappa (self.mapp2p) un gnodo di livello l che sia un partecipante attivo. Il più vicino possibile all'identificativo richiesto, cioè IP[l].
Partendo dal livello 3, in questo caso trovo di sicuro che il ID = 1 si presta bene, perché è lo stesso mio gnodo di livello 3 ed io sono un partecipante. Quindi passo al livello 2. Anche qui trovo che ID = 2 si presta bene, per lo stesso motivo.
Arrivato al livello 1 cerco un ID vicino a IP[1], che è 12. Posso vedere nella mia mappa se il gnodo 12, all'interno del ggnodo 2, all'interno del gggnodo 1, è partecipante al servizio. Se non lo è provo con 11, poi con 13, poi con 10, poi con 14, e così via.
Se arrivo ad un gnodo partecipante, mettiamo ad esempio 5, che è diverso da quello a cui appartengo io (self.mapp2p.me[1] = 3) allora non posso scendere ad un dettaglio più basso, perché nella mia mappa non ho informazioni sui nodi di quel gnodo. Quindi interrompo qui.
In questo caso, quindi, il valore ritornato da H([34,12,2,1]) sarà la lista [None,5,2,1]. Il valore None all'indice 0 mi dice che non ho dettagli a quel livello.

Attenzione ora. Io so che devo chiedere informazioni o servizi al nodo 1.2.12.34, oppure al nodo partecipante più "vicino" (per vicino si intende qui in senso di numeri, concetto molto astratto). So che il gnodo di livello 1 e indirizzo 1.2.5 contiene alcuni nodi partecipanti, ma non posso sapere quale di questi sia il più "vicino" al 1.2.12.34.
Il mio messaggio deve quindi giungere al primo border node che incontro in quel gnodo. Ma il protocollo TCP/IP non mi consente di specificare una tale destinazione. Per questo motivo ho bisogno di un metodo che instradi i miei pacchetti passo passo verso il border node di cui parlavo.
Vediamo come si realizza questo.
Il metodo msg_send ha come argomenti il mittente del messaggio (sender_nip=[4,3,2,1]) il destinatario "perfetto" del messaggio (hip=[34,12,2,1]) e il messaggio (msg) che è una tupla (func_name, args).
Per prima cosa trasforma il hip con il metodo H sulla base delle conoscenze di questo nodo (vedi sopra). In questo caso lo trasforma in hip=[None,5,2,1]
Poi usa il metodo neigh_get. Questo, a partire da un hip (che potrebbe avere i livelli più bassi a None) restituisce l'oggetto Neigh del vicino da usare come gateway. Trovato questo vicino richiama su di lui lo stesso metodo msg_send, che è remotable, passando sender_nip, il nuovo hip e il messaggio msg. TODO: l'hip passato al metodo remoto è quello con dentro i possibili None. Questo non causa problemi la prossima volta che si esegue H? Forse andrebbe passato l'hip originale?
Dopo alcuni passi il metodo msg_send verrà richiamato proprio sull'host destinatario (quello perfetto o il più vicino partecipante). Dopo aver richiamato il metodo H il nodo si accorge di essere lui stesso il destinatario e che quindi deve eseguire l'azione richiesta.

Per eseguire l'azione richiesta richiama il metodo msg_exec. Questo usa il messaggio (la tupla func_name e args) con il suo metodo dispatch, essendo la classe P2P una derivata di RPCDispatcher. In questo modo viene eseguito il metodo remotable indicato nella chiamata del servizio.
Il valore restituito dal metodo remotable indicato nella chiamata al servizio viene a sua volta restituito dal metodo msg_exec; poi viene a sua volta restituito dal metodo remotable msg_send chiamato nell'ultimo passo dell'instradamento; viene poi restituito a ritroso dallo stesso metodo chiamato nei vari passi dell'instradamento, fino ad essere restituito dal metodo msg_send chiamato in modo locale sul nodo mittente. TODO: Domanda: non sarebbe più performante e meno pesante per l'intera rete se la risposta avvenisse in modo diretto dal destinatario al chiamante con una connessione diretta visto che ora il destinatario ha l'IP preciso del mittente nell'argomento "sender_nip"?

La chiamata a msg_send non viene fatta direttamente, si vuole fare uso del meccanismo del FakeRmt (vedi il modulo RPC). Si usa quindi il metodo peer per ottenere una istanza di FakeRmt che sia a conoscenza delle informazioni da passare al metodo msg_send.
Il metodo peer si può richiamare specificando l'argomento keyword key oppure direttamente l'argomento keyword hip. Di norma si usa specificando la chiave da cui calcolarsi l'hip a cui rivolgersi. TODO: anche in questo caso a partire da key si eseguono sia il metodo h sia il metodo H. Invece dovrebbe essere eseguito solo h per avere l'hip perfetto da passare al metodo msg_send. Giusto?
Ad esempio il servizio Coordinator si usa così:
   co2 = self.coordnode.peer(key = (lvl+1, newnip))
   newnip = co2.going_in(lvl)
   co2.close()

TODO: Proseguire.

Il metodo partecipate (oltre a memorizzarlo sulla p2pmap) richiama sui vari vicini il metodo p2p.partecipant_add (vedi il modulo RPC).

Il metodo p2p.partecipant_add (richiamato da un nodo remoto) si segna sulla mappa (se già non lo sapeva) che quel nodo partecipa, e propaga l'informazione ai vicini.

La classe P2PAll gestisce tutti i servizi p2p registrati.
Quando riceve dalla classe Hook il segnale HOOKED richiama il metodo p2p_hook. Questo riechiede al vicino "più vicino" (come gnodo) la mappa dei servizi noti e ne fa il merge con la propria. Poi esegue il P2P.participate su quelli che gli interessano. Infine emette il segnale P2P_HOOKED.

Netsukuku/ita/ModuloP2P (last edited 2009-06-01 14:33:06 by lukisi)