sabato 17 settembre 2016

Disabilitare la compilazione TypeScript in Visual Studio 2015

Come sviluppatore Microsoft, è normale che sia un fan "più-che-sfegatato" di Visual Studio; ritengo sia l'ambiente di sviluppo di gran lunga più completo e confortevole presente in commercio. Ma nonostante gli sforzi che il nuovo corso della "Grande M" sta facendo per migliorarlo ancora, denota anche qualche difetto e limite nel momento in cui si va al di la dei confini dell'ambiente .NET.

In particolare, se ci si trova a lavorare con Angular2 e TypeScript - benchè si sia ancora parzialmente sul territorio Microsoft dal punto di vista del linguaggio - lavorando con Visual Studio (versione 2015) ci si può scontrare con una serie di errori di compilazione dei file ".ts" e delle dipendende di riferimento.

Il ritmo con cui vengono rilasciati gli aggiornamenti delle dipendenze e del compilatore TypeScript, è troppo elevato per poter adeguatamente tenere il passo tramite un update del nostro amato IDE; forse anche per questo, il team di Redmond ha pensato di affiancare ad esso un ambiente di sviluppo più leggero, semplificato e flessibile denominato Visual Studio Code, sul quale potrei spendere solo ottime parole.

Ma la mia intenzione in questo post è porre l'accento sulla problematica sopra menzionata: si sta realizzando una applicazione Angular2, usando un template ASP.NET MVC, ma Visual Studio 2015 non sembra essere è in grado di soddisfare la compilazione client-side seppur non ci siano formali errori.

In questi casi la soluzione migliore - a mio parere - è lavorare con il doppio IDE: VSCode per sviluppare i moduli Angular2 con TypeScript (e ovviamente compilarli, bundlizzarli e fare unit test) e Visual Studio 2015 per compilare l'applicazione "host" .NET che ospiterà tutti i file che finiranno sul server di produzione (magari con qualche feature interessate fornita server-side). Questo ovviamente significa dover disabilitare la compilazione di TypeScript in VS2015, in modo che almeno l'operazione di building del codice C# vada a buon fine.

Non essendo presente un opportuno setting tra i menu dell'IDE per raggiungere lo scopo, è necessario agire in maniera più "artigianale", editando il file ".csproj" con Notepad (o altro editor), raggiungere il primo nodo XML denominato PropertyGroup (solitamente è quello senza alcun attributo aggiuntivo) e cambiare il valore del tag TypeScriptCompileBlocked - che come è facilmente intuibile, blocca i tentativi di compilazione dei files TypeScript - dal valore di default false a true. Ad esempio:

E' da sottolineare che gli errori di compilazione TypeScript saranno ancora visibili nel pannello Error List di VS2015 - putroppo ve li dovete tenere - ma la compilazione del vostro progetto andrà a buon fine, e con essa una riduzione significativa della quantità di imprecazioni nella vostra giornata...

Alla prossima!
M.

sabato 11 giugno 2016

Utilizzare il nuovo Router di Angular2 (versione RC)

Nel precedente post (link) ho mostrato come integrare un componente JavaScript (per esempio MomentJs) con l'ecosistema TypeScript di Angular2. Questo approccio potrebbe essere molto utile nel primo periodo di utilizzo di questo framework, vista la momentanea scarsità di plugin nativi rispetto la controparte di AngularJs (prima versione).

Benché il funzionamento di NG2 sia molto diverso da AngularJs, molti concetti sono stati ripresi e opportunamente riveduti. Inoltre, se avete avuto esperienze di utilizzo di DurandalJs - altro fantastico framework JavaScript - noterete certamente alcuni aspetti che da esso sono stati acquisiti: l'ideatore di DurandalJs, Rob Eisenberg, ha lavorato fianco a fianco con il team di Angular2 per il primo anno di incubazione del progetto, per poi lasciare a causa di divergenza di opinioni. Ma la sua presenza ha fatto si che il nuovo sistema prendesse il meglio dei due mondi che fino a quel momento dominavano la scena per le Single Page Application.

Tra tutte queste novità, è naturale che anche il modulo Router abbia subito una radicale riscrittura, portando con sé numerose novità, e sopratutto continui aggiornamenti che si sono presentati fino alla Release Candidate di Angular2 (N.d.R. in questo momento siamo alla RC1). È così che con l'uscita dalla "fase beta" e l'inizio della fase "release candidate", il team di sviluppo di Angular ha scelto di marcare come "deprecated" (=obsoleto) l'implementazione del router presente fino a quel momento, a favore di una versione migliorata, non così profondamente differente.

Il problema più grosso di questa nuova implementazione non è tanto la diversità di funzionamento, ma quanto la difficoltà di reperire documentazione a riguardo su come riadattare il codice scritto in precedenza con questa nuova versione. Con un po' di fatica - e molto "googling" - le informazioni si trovano, ma la cosa richiede tempo: per questo motivo ho deciso di scrivere questo post, come mio personale "memento" e sperando di risparmiare a qualche collega del tempo prezioso.

Come dicevo, l'approccio è molto simile. Basta innanzi tutto rimuovere i riferimenti a @angular/router-deprecated - che i ragazzi di Angular hanno comunque lasciato disponibile nelle ultime release del framework - e referenziare il nuovo @angular/router.

A seguire riporto il codice ("quasi" completo) di una minuscola applicazione di tre pagine: una home, una pagina secondaria, e una di dettaglio che prende un parametro dalla querystring: spero apprezziate il mio sforzo nello "spirito meno parole e più codice" ;) .


Le differenze sono quasi impercettibili, come potrete notare già da "app.component.ts":
  • il vecchio "RouteConfig" usato per definire i percorsi di navigazione è diventato "Routes"
  • la route di default è sempre "/", visto che è stata rimossa la proprietà "useAsDefault"
  • è stata rimossa anche la proprietà "name" a favore dell'utilizzo del "path" (personalmente non l'ho mai capita la scelta di utilizzare "name" per riferirsi ad una particolare route)

Conseguenza delle modifiche di cui sopra, la direttiva per la costruzione dei percorsi (mi riferisco a routerLink) utilizza direttamente il path di navigazione, compresivo di eventuali parametri (molto più intuitivo, no?).

In ultimo, cambia radicalmente il modo di intercettare i parametri di route nel componente che ne fa uso (nel nostro esempio in "detail.component.ts"). Sarà necessario implementare l'interfaccia OnActivate (o "CanActivate" o "CanDeactivate") e utilizzare le funzionalità di RouteSegment.

L'impronta data dal team di Angular2 al nuovo framework mostra senza dubbio una visione molto ampia. C'è la sensazione che questo gioiellino, dopo un normale periodo di rodaggio, darà delle soddisfazioni e permetterà veramente di abbracciare lo sviluppo web senza piegarsi troppo ad una serie di compromessi che - fino a questo momento - abbiamo dovuto forzatamente accettare. Ora attendiamo l'uscita della versione ufficiale e definitiva...e partiamo!
M.

domenica 22 maggio 2016

Angular2 - Pipes e integrazione con librerie Javascript

Ormai da qualche mese mi sto "dilettando" con Angular2. Essendo un grandissimo fan (ed utilizzatore) di AngularJs, mi sembrava una naturale evoluzione; questo senza dimenticare che questa nuova reincarnazione del gioiellino di Google, chiude le porte con il passato, rivisitando completamente la maggior parte dei concetti e la sua infrastruttura in nome delle performance e dell'estendibilità.

Con questo post - giuro che questa volta cercherò di essere più breve del solito - non è mia intenzione fare una panoramica di Angular2; navigando in rete si possono facilmente trovare moltissimi articoli (molti dei quali veramente ben scritti) che fanno introduzioni dettagliate al framework, e imbastiscono in maniera esauriente il classico "Hello World, Angular2!".

Vorrei quindi condividere un paio di pensieri in merito ad una delle funzionalità che più sto apprezzando di NG2 e che - in qualche modo - ereditano da AngularJs prima versione: le Pipes, precedentemente note come "Filters".

Per illustrare questa funzionalità utilizzerò una situazione reale in cui mi sono trovato. Avevo un classico back-end realizzato con ASP.NET Web Api - ovviamente REST - e una serie di emissioni di strutture JSON; alcune di esse - come è normale - ritornavano date e orari nel classico formato stringa ISO (es. "2016-05-22T18:40:02.926Z", il default di Json.NET). L'esigenza era quella di renderizzare sulla UI tali componenti di data/ora, nel formato che meglio si adatta alla situazione.

Mi direte: "E qual'è il problema? Basta usare la pipe 'date' già presente nel "core" di Angular2, e tutto si risolve.". L'affermazione è vera solo in parte; questo perchè la pipe in "bundle" con NG2 (almeno per l'attuale versione RC1) non è in grado di gestire il formato ISO delle date che ho riportato poco sopra. Bisogna prima riconvertirlo in un oggetto "Date" Javascript (o TypeScript) e poi usarlo come binding per la UI per fare in modo che funzioni. Ma ovviamente questo vuol dire perdersi la possibilità di usare direttamente il modello proveniente dal server, scrivendo codice aggiuntivo per venire incontro all'inconveniente.

Ho deciso quindi di indirizzare la questione con una pipe custom, che sfrutta le potenzialità di MomentJs direttamente da TypeScript.

Per prima cosa - come potete vedere nel codice a seguire - dichiaro la variabile "moment", presente a livello globale perchè ho incluso nella mia pagina host il relativo file di MomentJs (direttamente il file ".js"); quindi dichiaro la Pipe custom (che con poca fantasia ho chiamato MomentDatePipe), che implementa PipeTransform. L'unico metodo richiesto per implementare l'interfaccia in questione è la dichiarazione di transform, che prende in input il valore di binding presente nel modello (nel mio caso una stringa con la data ISO), un "exponent" (che è solitamente un parametro passato alla pipe ed è facoltativo), e restituisce un valore (nel mio caso un'altra stringa con la formattazione di uscita).



Gli script aggiuntivi sono la dichiarazione del componente che farà uso della pipe (che riporta semplicisticamente la stringa data ISO sorgente), e il suo template HTML dove utilizzo in maniera dichiarativa la pipe appena realizzata, sfruttando l'alias riportato nel metadato di dichiarazione (=> @Pipe({ name: 'momentDate' })). Non dimentichiamoci di dichiarare l'uso della pipe nel componente: se non lo facciamo, il componente stesso non sarà in grado di trattare in maniera corretta l'alias dichiarato!

Con poche righe di codice, ho compensato una limitazione di Angular2; anche se sono abbastanza sicuro che con l'uscita della versione definitiva il team di sviluppo di NG2 avrà egli stesso provveduto a indirizzare la problematica in maniera più elegante.
Happy NG2!
M.

sabato 8 agosto 2015

RESTful Stress: misurare le performance di un servizio REST

Nel post di qualche settimana fa (lo potete trovare a questo link) abbiamo fatto una rapida introduzione al tema delle misurazioni delle prestazioni di una piattaforma applicativa fortemente basata su REST. Il tema è estremamente complesso, e le quattro idee che ho "buttato giù" sono solo la superficie di un mondo vastissimo ed articolato.

Al mio primo approccio con questo argomento - non lo nego - mi sono trovato parecchio spiazzato. Sono sempre stato abituato a "calcoli insindacabili" e certezze; ma tutto questo richiede una buona dose di analisi dei comportamenti e di...speranza.

Dico questo per un motivo molto semplice: rispondere alla domanda "quanti utenti l'ora è in grado di supportare la mia applicazione?", non è così banale. Vuol dire prima misurare tutto il misurabile, e poi dover fare i conti con la variabile più imprevedibile con cui un professionista dell'IT deve fare i conti: il comportamento dell'utente medio.

Se per la "variabile utente" non potete fare altro che osservare e ipotizzare che venga fatto un uso normale del vostro sistema, per le misurazioni - forse - può darvi una mano una piccola Chrome Application (originariamente detta "Chrome Packaged App") a cui ho iniziato a lavorare nel 2013 e che ho recentemente reingegnerizzato: RESTful Stress.

In breve. Si tratta di uno strumento in grado di simulare le chiamate HTTP verso una piattaforma di servizi REST, usando il componente XHR (XMLHttpRequest) del browser. In parole povere, fa esattamente quello che farebbe uno script jQuery client-side (una cosa del tipo "$.ajax(...)" ecc.) che invia e riceve dati da un server remoto.

Mi chiederete: "E dove sta la differenza?". È in grado di iterare la vostra chiamata HTTP (una o più chiamate) numerose volte, campionando l'orario di inizio, di fine, la durata, lo stato e il contenuto della response. Una volta rilevate le informazioni è in grado di produrre un feedback relativo alle performance che può essere facilmente e rapidamente interpretato.

Una volta installato RESTful Stress (versione 1.2.0) dal Chrome Web Store (a questo indirizzo), lo avviate, e vi troverete davanti a questa prima pagina:


Non complichiamo subito le cose: tralasciamo la modalità di misurazione presente in alto a sinistra (quella con " Atom", "Massive" e "Scenario", per intenderci) prendendo in considerazione il default "Atom". Come è possibile vedere c'è tutto l'occorrente per definite le specifiche di una chiamata HTTP:
  • Metodo HTTP, o "Verb": GET, POST, PUT, DELETE i più usati
  • Headers, in formato JSON, per maggiore flessibilità
  • Body, anche questo in JSON visto che siamo su REST
  • With credentials, ovvero forzando il passaggio di cookie autorizzativi ottenuti in una precedente iterazione
  • URL di destinazione (beh....ovviamente)
Nel box posto in alto alla view in questione, sono presenti le specifiche dell'iterazione di RESTful Stress: numero di cicli di esecuzione ("iterations"), intervallo tra una iterazione e la successiva ("delay"), "timeout" oltre il quale una chiamata HTTP è considerata fallita, e numero di iterazioni considerate "warmup" del sistema.

Solo una piccola parentesi su questa ultima specifica ("warmup", appunto), per sottolineare come - sovente - quando si invoca un servizio web (di qualunque genere) è normale che le prime chiamate siano particolarmente lente rispetto alle successive. In particolare, in ambiente .NET, IIS ha un comportamento tale che porta ad inizializzare alcuni componenti applicativi solo alla prima esecuzione degli stessi. E' così quindi che, durante i primi cicli di esecuzione, la nostra applicazione sembra essere particolarmente letargica: in realtà è sono in fase di "risveglio" prima di prendere il giusto ritmo. E' un diesel, se vogliamo...

A fondo pagina c'è tutto l'occorrente per non perdere le nostre impostazioni: "Store settings", che permetterà di salvare le specifiche nella storage area di RESTFul Stress (unitamente ad un nome mnemonico), e due comodi pulsanti per scaricare gli stessi settings su un file del vostro computer locale ("Download"), e per poi ripristinarli in seguito ("Upload").

Una volta impostati i dettagli, non resta altro da fare che cliccare il pulsante "Play", ed attendere che il tool faccia il suo sporco lavoro, eventualmente fermandolo con "Stop" (attenzione: bisognerà attendere la fine dell'esecuzione della request per avere l'arresto effettivo) o "Reset".

Completata l'esecuzione dei cicli (o durante il progress...e chi ce lo vieta?) possiamo analizzare la "history" delle chiamate; ciascun elemento è caratterizzato da :
  • codice di Status HTTP recuperato dal server: 200, 401, 404, ecc...(eventuali status codes con valore "0" rappresentano sempre una request mai effettuata)
  • orario di inizio della request verso il server
  • durata in millisecondi, ovvero il tempo tra l'invio della request e la ricezione della corrispondente response
  • lunghezza in bytes del contenuto della risposta
...e un comodo pulsante per visualizzare il contenuto del body della response stessa.


Utilizzando gli appositi "commands" localizzati nella parte alta della pagina, per non perdere i risultati del proprio lavoro, è possibile estrarre tutte le informazioni raccolte in formato JSON ("Download"), ricaricarle in RESTFul Stress per una consultazione successiva ("Upload") oppure produrre il più classico dei file CSV con un subset dei dati in elenco.

La scelta di JSON come formato di esportazione dei risultati di esecuzione non è casuale: spesso mi sono trovato a dover utilizzare gli stessi dati in altre applicazioni JavaScript-based (es. NodeJs o AngularJs) o .NET (usando il meraviglioso componente Json.NET): quale miglior formato per manipolare dati privi di uno schema predefinito?

La pagina "Chart" mostra in formato grafico l'andamento delle iterazioni sul servizio: in un secondo è possibile evidenziare eventuali deterioramenti della performance della piattaforma (magari per risorse che non vengono rilasciate correttamente), picchi di utilizzo, eventuali response con codici di errori (rappresentati con il colore rosso), e - ovviamente - le iterazioni che abbiamo battezzato nella pagina di settings come "warmup" del sistema (colore azzurro); queste ultime - come detto - vanno correttamente pesate nelle nostre considerazioni, essendo figlie di una fase di inizializzazione del sistema, e che quindi non rappresentano la reattività dello stesso a regime.


Il grafico è corredato di alcune misurazioni "grossolane" delle tempistiche: valori medi che danno un'idea della reattività della piattaforma, e permettono di iniziare a fare qualche considerazione in più rispetto i concetti espressi nel precedente post.

Facciamo un salto in avanti, e arriviamo alla sezione che permette di ripristinare i settings salvati nella prima pagina di RESTful Stress. Mi sembra una grossa perdita di tempo illustrarvi il suo contenuto: l'immagine parla da sola, e con essa il funzionamento di questa feature.


No, non mi sono dimenticato della pagina "Performance": è senza dubbio la parte più interessante e quella per cui avrete probabilmente iniziato a leggere questo post. Essa porta con se molte considerazioni, calcoli, manipolazioni e spiegazioni che richiedono il loro tempo...e non quattro concetti buttati sul piatto in malo modo. Me li custodisco come "succosi" argomenti per il prossimo articolo...

Buone ferie a tutti...
M.

sabato 11 luglio 2015

Load tests, Stress tests e performance di un servizio REST

A fine 2012 ho iniziato ad avvertire una maggiore sensibilità dei clienti riguardo il tema delle prestazioni di un sistema basato sul web. Parecchi degli applicativi a cui stavo lavorando in quel periodo erano già basati su architetture con piattaforme REST e dati in formato JSON, che si adattavano molto bene al tema della "misurazione dei tempi di risposta". La separazione dei moduli applicativi e - in particolare - del "presentation layer" dallo strato di servizi, è a mio parere una caratteristica essenziale per ottenere una metrica dell'applicazione sotto carico.

Faccio subito una precisazione ritengo doverosa. In ogni situazione - ed in particolar modo se si lavora con SPA (Single Page Application) - è fondamentale distinguere tra le problematiche prestazioni dovute ad un codice client "lento", magari non adeguatamente ottimizzato, da una piattaforma di servizi che fatica a fornire risposte nei tempi desiderati.

In questo post mi soffermerò su questa seconda casistica, poichè i problemi di performance lato client sono molto complicati da individuare, e spesso sono strettamente legati alla tecnologia (o al framework client, nel caso di JavaScript) che si sta utilizzando.

Ma ritorniamo rapidamente al nostro server. Immaginiamo di avere un backend basato su ASP.NET WebAPI: essendo sviluppatore .NET mi sembra la scelta più naturale. Abbiamo già innestato controller applicativi e le action che emettono correttamente i nostri flussi dati in JSON. Supponiamo di riuscire a tracciare un elenco delle request che una normale navigazione della nostra applicazione produce durante l'utilizzo della UI grafica. Questo tracciato di request (e opportuni verb, header e parametri) rappresenta una "sessione utente": questo è il dominio che dovremo andare a considerare per fare la nostra misurazione delle tempistiche di risposta.

Un esempio parla più di mille teorie, si dice di solito...ed è vero! Quindi ipotizziamo di avere un classico portale di commercio elettronico che permette la navigazione del catalogo, la scelta di uno o più prodotti da mettere nel carello virtuale, l'accesso autenticato tramite username e password, e l'operazione di checkout con acquisto dei prodotti. Ignoriamo l'interfacciamento con i sistemi di pagamento tramite carta di credito: è troppo complicato e per nulla utile in questo caso...
Una normale sessione utente (o "user session") è composta da questi passi funzionali:
  1. Visualizzazione della pagina principale del sistema e scaricamento della lista dei primi 10 prodotti più venduti presenti nello storage
  2. Navigazione al catalogo e visualizzazione delle categorie merceologiche
  3. Visualizzazione dell'elenco dei prodotti presenti nella categoria
  4. Dettaglio della scheda di un prodotto, sue specifiche e descrizioni
  5. Aggiunta di quel prodotto nel carrello elettronico
  6. Inserimento di username e password utente (ipotiziamo che il profilo utente sia già stato creato)
  7. Conferma dell'acquisto con la funzione di "checkout"
I passi riportati sopra rappresentano un comportamento "standard" di un utente del nostro portale, e - almeno in questa fase iniziale - non complichiamo troppo le cose pensando che ci possono (e ci saranno) delle naturali deviazioni rispetto questo specifico tracciato (es. navigazione più lunga, visualizzazione di più schede di prodotti, ecc.).

Quello che conta è "simulare" grossomodo quello che comporta una navigazione "da manuale", per farsi un'idea di quante "user session" contemporanee il nostro sistema è in grado di supportare senza creare disservizi o impattare negativamente sulla user experience dell'utente.

Mi rendo conto del fatto che, tutto quello che sto descrivendo, suona un po' "impreciso" e "ipotizzato"; ma vi posso assicurare che la predizione dei carichi a regime di una applicazione web, non è una scienza esatta. Le variabili sono migliaia e - cosa più importante - la componente fondamentale che farà pesare da una parte o dall'altra i vostri risultati e il comportamento umano, per definizione impredicibile ed incontrollabile. Quindi, il meglio che potete fare è cercare di approssimare con basso margine di errore, sperando che le eccezioni - che comunque ci saranno - non vadano a pesare in maniera troppo rilevante sul numero finale.

Quello che bisogna fare da questo punto in poi è certamente più matematico, e meno da "chiromante" ;). Si tratta di mettere un numero (in millisecondi) di fianco ad ogni punto del precedente elenco. Come? Facile: rilevando l'orario corrente prima dell'esecuzione della request lato client, e dopoaver ricevuto la relativa response; la durata di ogni singolo step è presto misurata.

E' fondamentale calcolare altri due indicatori : l'average duration di una request (la durata media), e il throughput, cioè il numero di richieste al secondo che la nostra service platform è in grado di erogare:
  • [average duration]: tempo medio di durata, somma delle durate (in millisecondi) delle varie richieste presenti nel nostro scenario di "user session", diviso per il numero di richieste...che ve lo dico a fare? ;)
  • [throughput]: calcolato come 1000 / [average duration], dove il valore 1000 rappresenta il numero di millisecondi in un secondo, il tutto misurato in "richieste al secondo"
L'obiettivo finale è capire quanti utenti concorrenti il nostro sistema è in grado di sostenere. E per fare ciò è necessario usare la Legge di Little, che dice: il numero di utenti simultanei reali ("concurrent users") che un'applicazione è in grado di supportare è definibile con la formula:
  • [concurrent users] = [throughput] / λ
con il simbolo λ che rappresenta la media del valore di arrivo ("arrival rate") delle richieste. Questo "arrival rate" è l'inverso dell' inter-arrival time, cioè il tempo (misurato in millisecondi) che trascorre tra una richiesta e la successiva.

E' giunto il momento di fare una considerazione estremamente importante: esiste una netta distinzione tra Stress Test e Load Tests. I primi sono mirati a trovare il punto di rottura di una applicazione, cioè a sovraccaricare un sistema al punto tale da raggiungere una situazione nella quale il servizio stesso non viene più erogato. I test di carico (o "Load Tests") sono invece mirati a misurare il numero massimo di utenti (o sessioni utente) che l'applicativo è in grado di sostenere in una porzione di tempo stabilita, erogando il servizio definito dal processo di business.

I test di carico devono quindi rappresentare una stima (ovviamente simulata ed approssimata) di un utilizzo reale. E realisticamente parlando, è abbastanza inverosimile che le request alla piattaforma WebAPI arrivino in continuazione, senza sosta alcuna. Va quindi considerato un altro parametro (ancora un'altro...mi spiace) che rappresenta l'intervallo tra l'arrivo di una response, e l'inizio della request successiva: questo parametro è solitamente chiamato "think time".

L'inter-arrival time che abbiamo citato qualche riga più sopra, è calcolato sommando la durata media dei una singola request/response al server, al think time. E il think time a sua volta è stabilito a tavolino - non calcolato - e basato su un'osservazione del comportamento di modelli simili in situazioni reali; il che vuol dire che la sua durata è solitamente rilevata su dati prelevati a campione durante l'uso di una applicazione.

Ipotizziamo quindi che il "tempo di riflessione" (traduzione orribile, me ne rendo conto) sia di 5 secondi; ed è una casistica tutto sommato verosimile quella per cui possa arrivare - in media - una request ogni 5000 millesimi di secondo alla piattaforma WebAPI. Quindi, riassumendo:
  • [inter-arrival time] = [think time] + [average request duration]
  • [arrival rate] = 1 / [inter-arrival time]
Il calcolo del numero di utenti - come abbiamo visto - è quindi strettamente legato alla durata di esecuzione della singola richiesta, e dal tempo di intervallo stimato tra una richiesta e l'altra. Ed essendo quest'ultimo una stima, non è possibile dare una risposta precisa ad un vostro eventuale cliente che vi chiede : "Quanti utenti è in grado di supportare la mia applicazione?". Potete rispondere solo avvalendovi di alcuni parametri dettati dall'esperienza (vostra o di altri prima di voi), misurando quanto è effettivamente misurabile, ma nulla di più.

Ma anche solo misurare quel poco che è certo, non è una cosa banale. Ed è per questo che a metà 2013 ho iniziato un piccolo progetto, di cui ho giù parlato in questo post, chiamato RESTful Stress, disponibile sul Chrome Web Store come Chrome App.

Come si usa? Non è nulla di complicato...ma ne parleremo la prossima volta...
M.

sabato 11 aprile 2015

Upload di un file e un form su una action ASP.NET Web Api con MultiPartFormDataContent

Lavorando con ASP.NET Web Api vi sarà sicuramente capitato di dover caricare lato server un file presente sul computer dell'utente. Se state usando un client basato su JavaScript - magari jQuery, se non volete farvi del male con XHR - è certamente una cosa piuttosto semplice: sono decide, per non dire centinaia, i controlli che si trovano in rete che già fanno quello che vi serve; basta trovare quello giusto che risponde alle vostre esigenze, aggiungere il riferimeto allo script, e....via!

Se poi, come sovente accade, dovete passare anche dei dati "form" nella richiesta di upload - magari per associare il file caricato sul server ad una entità presente nella vostra base dati - l'operazione richiede qualche sforzo in più, pur rimanendo fattibile e molto documentata. Parlo sempre di JavaScript, eh...

Ma se dovete fare questa cosa da .NET, magari un'applicazione WPF o una semplice console application, anche eseguire un semplice upload non è banalissimo. I post che si trovano in rete sono talvolta discordanti, e tendono a confondere il lettore...sopratutto se il lettore sono io ;) .

Detto questo, dopo numerosi tentativi e un paio di orette perse, sono riuscito a trovare il "bandolo della matassa". Ma non perdiamoci in chiacchere e vediamo subito un po' di codice. Questa è la action Web API che ho realizzato lato server per un recente progetto (opportunamente semplificata, chiaramente): Battezzata la mia action come un "POST" e un nome auto-esplicativo, subito è da notare che il valore di uscita è un "Task": questo nel puro spirito di "asincronicità" che è richiesto ad un servizio che eroga dati. Comunque...il codice è abbastanza chiaro: faccio un paio di verifiche sul MIME del contenuto ricevuto, stabilisco il percorso della folder temporanea dove verrà scritto lo stream del file caricato in upload, quindi leggo i dati "form" dove è contenuto il mio identificativo per l'oggetto business a cui dovrò associare il file ricevuto (un generico "Document", in questo caso).

E' importante che il metodo sia in grado di rispondere al client seguendo la semantica HTTP quindi ritornare un 404 "NotFound" se l'oggetto richiesto non esiste, un eventuale 401 "Unauthorized" se l'utente connesso al sistema non ha i diritti per compiere l'operazione, ecc. Ho volutamente "tagliato" il discorso delle permissions per non perdere il focus del post...

In ultimo scorro i contenuti "FileData" del provider di gestione e recupero sia il contenuto binario del file (o dei files) contenuti nella request, sia il nome del file specificato dal client che ha inviato la richiesta stessa. Il resto è semplice: lettura del contenuto di stream e associazione ad una nuova entità "business" che conserverà il file (lo "Scan") e salvataggio della stessa su disco o sulla base dati previa validazione delle informazioni.

Una piccola nota a valle del salvataggio dell'oggetto "Scan". Il metodo "ApplicationServiceLayer.SaveScan" emette una lista di "ValidationResult" per ogni errore di salvataggio o validazione fallita sull'entità: è buona regola inviare queste validazioni al client con un codice 400 "BadRequest", aggiungendo alla response i messaggi di errore generati. Lato client li potrete facilmente intercettare e gestire (magari per mostrarli direttamente all'utente finale).

Una volta definita la action server, passiamo a quello che ci interessa di più: il client. Ma prima una rapida domanda: come gestiamo i differenti HTTP status codes (400, 401, 404, ecc) che vengono generati nel caso in cui non tutto andasse liscio? L'argomento è molto dibatutto, e la mia personalissima opinione è che in un ambiente come .NET, tutto quello che non va come dovrebbe andare, va tratto come un'eccezione.

Ma dobbiamo ricordarci anche che il client dovrebbe implementare il pattern "async/await" per non ledere la user experience dell'utente. Ho quindi implementato un'eccezione "non standard" in grado di recuperare quante più informazioni possibili dal server, in presenza di uno status code diverso da 200 "OK": Passiamo ora all'implementazione del client. Si, l'ho detto anche prima: ma stavolta tengo fede alla promessa... Il metodo deve essere estremamente semplice da utilizzare: bastarà istanziare la classe (che potrebbe tranquillamente essere statica), passare come parametri l'identificativo del documento a cui il file va associato, il nome del file e il suo contenuto in bytes...et voilà!

L'implementazione non è altrattanto banale: validazione dell'input (un "must" per un buon stile di programmazione), creazione dell' HttpRequestMessage - chiaramente con verb "POST" e indirizzo completo dell'endpoint della action che abbiamo lato server - e creazione del contenuto della request...la parte più "succosa".

Il corpo della richiesta va riempito con un MultipartFormDataContent: una comodissima classe .NET che permette di wrappare una serie di differenti informazioni da "impacchettare" ed inviare al server. Essa funge da scatola sia per i contenuti binari (il nostro file), sia per le informazioni di "form" (l'id del documento da associare). Basta crearne un'istanza passando una stringa di "boundary" (una sorta di separatore per gli elementi contenuti nel body della "multipart request"), quindi inserire i vari contenuti.

Il primo elemento da aggiungere al MultiPartFormDataContent è di tipo ByteArrayContent: servirà per contenere i dettagli del file che vogliamo inviare al server. Passando i bytes del contenuto e il nome del file, non dobbiamo dimenticare di importare il "Content-Type" nell'header del contenuto stesso: servirà al server per stabilire la "natura" dei dati che riceverà. Quindi aggiungiamo tutto l'elemento al "multipart" container specificando la stringa "File" come nome dell'elemento trasmesso.

In seconda battuta inseriamo un contenuto testuale con StringContent: anche in questo caso bisognerà specificare il valore dell'identificativo del documento come contenuto, delegando il "name" del contenuto stesso ("documentId") all'aggiunta di StringContent sul multipart.

Ora che il nostro body della request è pronto, è sufficiente aggiungerlo al messaggio creato in precedenza, istanziare il client HttpClient, quindi inviare il tutto lato server restando in attesa con la keyword "await". Come anticipato qualche riga sopra, la response potrebbe essere uno status code di successo (200 - OK) che determina un'operazione lato server andata completamente a buon fine, oppure uno dei codici di "fail" che abbiamo gestito nelle WebAPI.

In caso di fallimento è necessario far entrare in gioco l'eccezione custom che abbiamo realizzato prima: diamo in pasto la request al metodo GetWebApiException: Il contenuto della response ricevuto dal server sarà deserializzato dal formato JSON (usando la meravigliosa libreria Json.NET), quindi saranno estratti tutti gli eventuali messaggi di errori presenti nel flusso ricevuto. L'implemetazione di WebApiException sarà in grado di gestire in maniera tipizzata le informazioni, di fatto rendendole usabili per la visualizzazione sul client.

Come detto in precedenza, ritengo che uno status code non di successo debba essere gestito tramite un eccezione a livello client-side: sta quindi allo sviluppatore eseguire queste chiamate in blocchi try/catch, all'interno dei quali poter intercettare eccezioni di tipo WebApiException, e trattare il loro contenuto nella maniera più consona alla situazione.

Spesso, anche cose semplici come l'upload di un file su un server, possono diventare complicate se non si conosce perfettamente il funzionamento del protocollo HTTP. Spero che questi appunti - perchè solo di questo si tratta - possano essere utili a qualche collega che - come il sottoscritto - ha speso inutilmente ore per un'inezia...
M.

giovedì 12 marzo 2015

Language Integrated Query con JavaScript

Secondo alcuni studi americani, già dal gennaio del 2014, l'utilizzo di Internet - intesa come volume di traffico - richiesto da dispositivi mobili (smartphones e tablet) ha superato la quantità richiesta da computer desktop e ambienti business (servers e quant'altro). Questo dato deve far pensare: si tratta di uno dei traguardi più significativi da quando la rete globale è entrata nella nostra quotidianità.

Una persona media fruisce la rete in maniera del tutto nuova, con una frequenza differente, durante tutto l'arco della giornata e non soltanto nelle ore lavorative. Internet ha raggiunto persone di ogni età, cultura e ceto sociale.

E' questo ha portato - e porterà sempre di più in futuro - alla necessità di realizzare strumenti più accessibili, che permettano la fruizione delle informazioni in maniera semplificata ed intuitiva. Questo, cercando di raggiungere più terminali, e quindi più persone possibili.

E' così che - da oltre quattro anni (dal lontano 2010) - tecnologie fortemente orientate al "client-side" sono diventate parte della mia vita di sviluppatore. HTML5 (nelle sue varie draft"), CSS3 e sopratutto JavaScript sono il pane quotidiano con cui è necessario confrontarsi per ottenere risultati efficaci.

Framework client side come EmberJs, KnockoutJs, ma sopratutto AngularJs (senza dubbio con maggiore potenzialità - IMHO) hanno monopolizzato lo sviluppo del "Presentation Layer", cercando di fornire una "struttura organizzata" ad un linguaggio come JS, che strutturato non è.

Ma questi framework - da soli - non sono sufficienti per coprire tutte le peculiarità di uno sviluppo orientato al client. Specialmente per uno sviluppatore come me - che viene dal mondo server - la mancanza di "facilities" di un framework robusto e completo come .NET, si sentono...eccome.

Tutto questo per dire cosa? Che ho voluto fare un esperimento, una prova, per capire se fosse possibile portare sul web uno dei miei "pezzi" preferiti di .NET. LINQ: "Language Integrated Query"...una lode a Microsoft e al suo ideatore!

E adesso mi direte: "Ma il porting di LINQ per JavaScript già esiste...". Verissimo...e in molte incarnazioni. Ma la mia premessa era quella di un esperimento, iniziato dalle fondamenta, che cerca di modellare un concetto come le lambda expressions, che in JavaScript, in un certo senso, esistono già dagli anni novanta.

E' così che qualche mese fa ho iniziato il mio progetto jslinq (che fantasia, eh? :) ). Sono partito con "where" e "select", per arrivare a "count": e alla fine mi sono trovato per le mani una piccola libreria che mi aiuta tantissimo ogni giorno.

Il passaggio su GitHub è stato d'obbligo, così come la condivisione del codice sorgente. Recentemente la pubblicazione su NPM (per metterlo a disposizione di Node.js) e Bower (il package manager più diffuso per lo sviluppo client-side).

Ammettiamo di avere una struttura dati in formato JSON (con ogni probabilità scaricata da un WebApi lato server): L'idea è quella di poter fare tutte le trasformazioni, filtri, ordinamenti ("magheggi", in termine tecnico) che si può fare con una lista analoga server-side con LINQ. Dopo aver referenziato jslinq nella vostra pagina web - o nel modulo di Node.js - è sufficiente "dare in pasto" il nostro array di dati al costruttore, in questo modo:

Da qui è tutto facile ed intuitivo: Con "where" possiamo applicare un filtro sui dati, simulando di fatto la lambda expression che avremmo fatto con LINQ con una semplice "function", il cui unico parametro è l'istanza di un singolo elemento presente nell'array di dati. Si applica la condizione di filtro - in questo caso imponiamo che la proprietà "name" del singolo elemento sia uguale a "two", e si ritorna il risultato dell'operazione di confronto come uscita della "function" stessa. Facile, no?

Il sistema di jslinq è implementato come "chainable" ("concatenabile") e segue il medesimo principio delle "fluent" e di jQuery, per intenderci (che oltretutto è il medesimo di LINQ). Conseguenza di ciò il risultato della funzione "where" è a sua volta un oggetto "jslinq" che deve essere elaborato per ottenere un dato valido: questa elaborazione del valore di uscita viene fatto con il metodo "toList", che emette solo gli elementi che sono stati precedentemente filtrati dalla clausola "where".

Ammettiamo di voler ragguppare i dati originali per Otterremo una nuova struttura delle informazioni che riporterà come "key" il campo (o i campi) che è stato selezionato per il "groupBy", il conteggio degli elementi contenuti in ciascun gruppo, e la lista degli elementi opportunamente classificati. Questo il risultato della "function" applicata poco sopra: Attualmente jslinq è alla versione 1.0.12 che personalmente considero stabile per un uso in ambiente di Produzione. Contiene più di una ventina di operatori, molti dei quali ricalcano il comportamento di LINQ ("count", "max", "min", "singleOrDefault", ecc...). Ma naturalmente le esigenze quotidiane hanno portato ad arricchire sempre di più la libreria di nuove funzionalità ("intersect", "substract", "remove", ecc...), che pur nella loro banalità, permettono di ridurre al massimo il codice JavaScript che è necessario scrivere per operare su array di elementi.

Quando ci si trova a lavorare rispettando alla lettera (o quasi) il principio di "Separation of Concerns", avendo accesso al workflow applicativo solo attraverso delle API che emetto dati JSON, si incorre spesso nella necessità di "raffinare" i dati sul client. Questo per alleggerire il server ed evitare di creare dei metodi API troppo complessi nel loro utilizzo, oppure costruire una moltitudine di differenti chiamate che fanno quasi la stessa cosa. E' questo il momento in cui jslinq può essere d'aiuto, delegando al client stesso parte di una elaborazione "minimale" che diminuisce la complessità del server, e pesa pochissimo sul client.

Bon...questo è quanto. Fateci un giro! ;)