• Non ci sono risultati.

1. MQTT-SN client : si connettono a un server MQTT attraverso un MQTT-SN gateway

4.3. Considerazioni su Californium

All'interno di Californium sono presenti tre componenti molto importanti che permettono di mantenere una certa flessibilità nella composizione delle applicazioni basate su CoAP: Lo schema di connessione di Resource, Endpoint e Connector è il seguente:

Resources : ogni server mantiene una struttura ad albero dove ogni nodo è una risorsa che

viene identificata da un indirizzo. Quando una richiesta arriva al server, esso ricerca la risorsa all'interno della struttura ad albero e se la trova elabora la richiesta e risponde con un codice di risposta adeguato (con relativo payload).

Endpoint : un endpoint integra tutta l'implementazione del protocollo CoAP. Si occupa della

decodifica del datagramma in un oggetto Request ed effettua il forwarding verso la Resource. Lo schema principale è il seguente:

I componenti sono:

1. Token : genera un ID per ogni nuova richiesta.

2. Observing : effettua la relazione tra client e Resource per la notifica delle modifiche. 3. Blockwise : permette di suddividere un messaggio di grandi dimensioni in pacchetti

più piccoli.

4. Reliability : permette la ritrasmissione di messaggi CON (messaggi che richiedono un

5. Deduplication matching : permette il riconoscimento di messaggi duplicati.

Abbiamo poi il Connector che si occupa di come il server invia e riceve i messaggi e gestisce il protocollo di trasporto ma non è a conoscenza del formato dei messaggi CoAP.

Inoltre non è una socket in quanto un endpoint non effettua una query verso il Connector per la ricezione di un altro messaggio ma è il Connector che richiama l’endpoint associato e gli passa i nuovi messaggi.

I metodi dei connettori sono non bloccanti e ogni Connector ha la propria politica di concorrenza.

Sono presenti, all'interno di Californium, diversi aspetti che possiamo considerare riguardo all'implementazione vera e propria:

• Utilizzando un singolo thread per tutte le elaborazioni potremmo liberarci di tutti problemi relativi alla concorrenza ma le prestazioni ne risentirebbero.

• Utilizzando un pool di thread per l'esecuzione concorrente, ad esempio di richieste indipendenti, con un numero variabile che può essere scelto in fase di elaborazione, su una macchina quad-core si è riusciti ad ottenere (con 4 thread) quasi il doppio delle prestazioni rispetto all'utilizzo di un singolo thread.

Nell'implementazione propria ogni Connector, ogni Endpoint e ogni Resource ha il proprio modello di concorrenza. Questo significa che è stato scelto un modello stage- based in cui ogni messaggio in arrivo e in uscita viene elaborato in serie dai tre componenti. Questa operazione potrebbe avvenire in parallelo ma non sarebbe efficiente in quanto verrebbero introdotti troppi context switch. In genere alcune risorse possono elaborare le richieste in modo concorrente mentre altre presentano delle sezioni critiche:

Se una risorsa non definisce un pool di thread viene ereditato il pool dal parent e se non è presente una risorsa che definisce un proprio pool di thread sul percorso associato allora verrà utilizzato lo stesso thread che elabora il protocollo CoAP.

È possibile semplicemente aggiungere la keyword "synchronized" alla dichiarazione del metodo per specificare una sezione critica da eseguire in single-thread.

Mentre nel caso di HTTP possiamo ottenere l'affidabilità grazie a TCP, in CoAP questo non è possibile e dobbiamo implementarla a livello applicazione.

Un endpoint deve essere preparato a ricevere lo stesso messaggio più volte e se un messaggio arriva una seconda volta il server deve rispondere con lo stesso ACK, RST o risposta in genere, senza eseguire la richiesta una seconda volta. È essenziale comunque che la rilevazione dei duplicati sia un'operazione atomica, in modo tale da eseguire soltanto una richiesta alla volta se arrivano contemporaneamente.

Californium memorizza tutti gli scambi in un oggetto di tipo ConcurrentHashMap, una struttura dati molto potente e altamente ottimizzata che supporta l'esecuzione concorrente. Infatti viene utilizzato il metodo putIfAbsent() per ricercare se un messaggio è già presente e inserirlo nella mappa se non lo è.

Per evitare che la struttura dati diventi troppo grande, un thread controlla periodicamente l'età di tutte le entry e se sono scadute le rimuove dalla HashMap. Questo modo di gestire gli intervalli di tempo è molto più efficiente rispetto a quello che utilizza un timer per controllare

la scadenza.

Californium permette di effettuare il wrapping del protocollo di trasporto all'interno di un Connector che richiama la propria strategia multi-threading. Si può cambiare in modo dinamico il numero di thread in un Connector, in modo tale da adattarlo ai differenti broker.

Un altro aspetto interessante è quello relativo ai Garbage Collector utilizzati da Java. Infatti quando Californium esegue molte operazioni contemporaneamente, effettua allocazioni e deallocazioni di grandi quantità di memoria.

Scegliere la strategia di garbage colletion appropriata può migliorare di molto le prestazioni e il fattore più importante è quello relativo al tempo di sospensione dei thread: quando

Californium ha a disposizione uno spazio di memoria più grande può elaborare più richieste prima che ci sia un'altra iterazione da parte del Garbage Collector. Ad un certo punto il GC sospende tutti i thread per avere accesso esclusivo alla memoria e questo si traduce in una maggiore latenza tra le operazioni vere e proprie del protocollo.

Per ottimizzare le prestazioni si può effettuare il tuning di alcuni parametri del GC. Si può scegliere infatti di utilizzare un algoritmo che opera in modo concorrente rispetto agli altri thread riducendo il tempo di sospensione di quelli di Californium.