Differences between revisions 7 and 10 (spanning 3 versions)
Revision 7 as of 2009-05-24 17:03:03
Size: 7633
Editor: lukisi
Comment:
Revision 10 as of 2009-05-25 09:46:42
Size: 10592
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 20: Line 20:
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. 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}}} della classe {{{MapP2P}}} appena istanziata) per gli eventi {{{ME_CHANGED}}} e {{{NODE_DELETED}}} della Maproute. In questo modo si incarica di tenere sincronizzate queste due mappe.
Line 27: Line 27:

=== Partecipanti al servizio ===

Per dichiarare che il nodo corrente partecipa ad un servizio peer-to-peer uso il metodo '''{{{partecipate}}}''' dell'istanza di {{{P2P}}} relativa a quel servizio. L'informazione si propaga attraverso il metodo remotable '''{{{partecipant_add}}}'''.

Il metodo {{{partecipate}}} memorizza sulla propria p2pmap (con il suo metodo {{{partecipate}}}) la propria partecipazione al servizio; inoltre richiama sui vari vicini il metodo {{{p2p.partecipant_add}}} (vedi il [[../ModuloRPC|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.

=== Richiesta di un servizio ===
Line 54: Line 64:
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 70:
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 74:
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>>
'''''TODO''': Il metodo neigh_get mi dice il mio vicino migliore per raggiungere il gnodo 1.2.5 - ma questo vicino non è detto che sia partecipante al servizio. Se non lo è allora la chiamata '''"n.ntkd.p2p.PID_"+str(self.mapp2p.pid)+".msg_send(sender_nip, hip, msg)"''' non funziona. Sbaglio?''
<<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.
Line 66: Line 80:
'''''TODO''': Proseguire.'' 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"?''
Line 68: Line 84:
Il metodo {{{partecipate}}} (oltre a memorizzarlo sulla p2pmap) richiama sui vari vicini il metodo {{{p2p.partecipant_add}}} (vedi il [[../ModuloRPC|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.

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ì:
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ì:
Line 82: Line 96:
~+La classe '''{{{P2PAll}}}'''+~ gestisce tutti i servizi p2p registrati. == La classe P2PAll ==
La classe {{{P2PAll}}} gestisce tutti i servizi p2p registrati.

Quando un nodo ha completato un ''hook'' alla rete, ad esempio dopo la fase di startup ('''''TODO''': Anche questo gestore dell'evento HOOKED dovrebbe essere registrato solo dopo il primo hook che avviene nella fase di startup solo per l'ativazione delle interfacce di rete. Vedi la [[../NtkNodeStartup|fase di startup]]'') viene richiamato il metodo microfunc {{{p2p_hook}}}.
Line 84: Line 101:
Quando riceve dalla [[../ClasseHook|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}}}. Questo metodo cerca un vicino, possibilmente del nostro stesso gnodo di livello 1, oppure del nostro stesso gnodo di livello 2, e così via. Al vicino "più vicino" (come gnodo) che trova, richiede la mappa dei servizi noti e ne fa il merge con la propria (vedi il metodo {{{map_data_merge}}} della [[../ClasseMap|classe Map]]).

Poi esegue il {{{P2P.participate}}} su quelli che gli interessano. Infine emette il segnale {{{P2P_HOOKED}}}.

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 della classe MapP2P appena istanziata) per gli eventi ME_CHANGED e NODE_DELETED della Maproute. In questo modo si incarica di tenere sincronizzate queste due 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).

Partecipanti al servizio

Per dichiarare che il nodo corrente partecipa ad un servizio peer-to-peer uso il metodo partecipate dell'istanza di P2P relativa a quel servizio. L'informazione si propaga attraverso il metodo remotable partecipant_add.

Il metodo partecipate memorizza sulla propria p2pmap (con il suo metodo partecipate) la propria partecipazione al servizio; inoltre 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.

Richiesta di un servizio

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?
TODO: Il metodo neigh_get mi dice il mio vicino migliore per raggiungere il gnodo 1.2.5 - ma questo vicino non è detto che sia partecipante al servizio. Se non lo è allora la chiamata "n.ntkd.p2p.PID_"+str(self.mapp2p.pid)+".msg_send(sender_nip, hip, msg)" non funziona. Sbaglio?
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()

La classe P2PAll

La classe P2PAll gestisce tutti i servizi p2p registrati.

Quando un nodo ha completato un hook alla rete, ad esempio dopo la fase di startup (TODO: Anche questo gestore dell'evento HOOKED dovrebbe essere registrato solo dopo il primo hook che avviene nella fase di startup solo per l'ativazione delle interfacce di rete. Vedi la fase di startup) viene richiamato il metodo microfunc p2p_hook.
Questo metodo cerca un vicino, possibilmente del nostro stesso gnodo di livello 1, oppure del nostro stesso gnodo di livello 2, e così via. Al vicino "più vicino" (come gnodo) che trova, richiede la mappa dei servizi noti e ne fa il merge con la propria (vedi il metodo map_data_merge della classe Map).

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)