posts - 644, comments - 2003, trackbacks - 137

My Links

News

Raffaele Rialdi website

Su questo sito si trovano i miei articoli, esempi, snippet, tools, etc.

Archives

Post Categories

Image Galleries

Blogs

Links

Asp.net MVC, immagini dinamiche e performance

La generazione di immagini dinamiche è un'esigenza piuttosto frequente che Asp.net MVC risolve in modo realmente triviale. Ovviamente le considerazioni che faremo valgono per qualsiasi file servito dinamicamente, per esempio per i Download.

È sufficiente creare il mapping del routing di asp.net e un controller in cui una action ritorna uno di questi oggetti:

  • FileContentResult, tipicamente utilizzato per restituire un byte array, magari letto da un database
  • FileStreamResult, comodo quando il file viene generato a partire da uno stream
  • FilePathResult, nel caso in cui il file sia fisicamente presente su disco
  • Una ActionResult custom, magari derivata da una delle classi sopra elencate (o dalla loro classe base FileResult)

Un esempio di controller che restituisce sempre lo stesso file può essere il seguente:

public class ImageDemoController : Controller { public ActionResult Get(string file) { return base.File(@"C:\Temp\image_22.png", "image/png"); } }

Fin qui tutto bene, il codice è semplice e perfettamente funzionante.

Il problema

Poi passa il tempo e quando si arriva a verificare le performance del sito ci si accorge con il profiler di Internet Explorer 9 che le cose vanno piuttosto male:

image

I contenuti statici, ma solo alcuni contenuti dinamici, vengono serviti in tempi rapidi come è logico aspettarsi per quelle dimensioni.
La barra di colore giallo evidenzia che il tempo è tutto speso nella request (la response è in colore blu). Questo significa che quel tempo è l'intervallo passato tra la richiesta del browser e l'inizio del trasferimento. Perciò nulla è addebitabile agli script, al browser o alla banda.

Se nel codice del controller vi fosse della logica più corposa (come è auspicabile :D) potremmo pensare di aver scritto qualche sciocchezza che ruba quella montagna di tempo.

La diagnosi

Il codice della nostra demo è troppo triviale ma in casi reali vorremmo eseguire una profilazione. Potremmo quindi utilizzare Ants di RedGate ma purtroppo verrebbe fuori che il nostro codice è così performante da non valere neppure la pena di apparire sul profiler.

Una seconda opzione per la profilazione, molto utile per misurare le performance di una request è MvcMiniProfiler, realizzato dal team di StackOverflow e disponibile su NuGet:

image

Non fa magie ma ci rende semplice collezionare i dati di profilazione ottenuti tra la Application_BeginRequest e la Application_EndRequest nel global.asax.cs. Basta chiamare, rispettivamente, la MvcMiniProfiler.MiniProfiler.Start(); e la MvcMiniProfiler.MiniProfiler.Stop();
Evito di scendere nei particolari perché in questo caso scopriamo che effettivamente la nostra request impiega il tempo già visto nel profiler di Internet Explorer ma non ci fornisce alcuna indicazione utile per capire dove viene speso.

Riassumendo. Alcune request di immagini dinamiche hanno un alto costo globale ma che non dipende dal nostro codice. Appare ovvio che bisogna scendere di livello.

Andiamo perciò a vedere cosa succede in IIS. Per questa diagnosi è necessario utilizzare IIS "full" e non la versione express.

Dalla mmc di IIS, abilitiamo FREB (Failed Request Tracing)

imageimage

E poi abilitiamo, via wizard, il log di tutte le richieste:

image image image

Questo step non ha ancora abilitato il logging ma lo ha solo configurato. Per "accendere" il log è necessario abilitarlo in questo modo:

image SNAGHTML14ef4cd5

Nota importante. Questa operazione eseguita su un server di produzione causa forti rallentamenti e la generazione di un altissimo numero di log. Sto perciò dando per scontato che venga fatto su una macchina di sviluppo o di test.

A questo punto è sufficiente un refresh della pagina incriminata e aprire la cartella dei log FREB appena configurata.

IIS genera automaticamente il file FREB .XSL che consente una visualizzazione "umana" dei log. Ogni request viene loggata in un file diverso.

Aprendo file per file, sulla destra si può immediatamente vedere quanto tempo è occorso, globalmente, per servire la request:

image image

Una volta individuata una immagine dinamica lenta, si entra nei dettagli:

image

Ed espandendo la request con il tempo più alto si può finamente capire la causa:

image

La creazione della Session, che pure non utilizziamo, inficia inutilmente sulla performance di queste request.

Ricordiamoci di non abusare mai della Session e che, pure utilizzando Cache come lo stesso team di Asp.net raccomanda, la session è utile per avere il SessionId. Disabilitarla in modo globale a mio avviso deve essere una decisione molto ben ponderata.

La soluzione.

La soluzione è tanto triviale quanto il codice che abbiamo già scritto: disabilitare la Session solo per le request che sono servite da quel specifico controller:

[SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)] public class ImageDemoController : Controller { public ActionResult Get(string file) { return base.File(@"C:\Temp\image_22.png", "image/png"); } }

Print | posted on martedì 5 luglio 2011 14:16 |

Feedback

Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Veramente interessante, mi era capitata una cosa simile in un progetto non MVC, in quel caso era un servizio asmx che era lento a causa della session.

L'abuso della session è spesso causa di molti mali :).
05/07/2011 14:50 | Gian Maria
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Eh si, ma credo che al di là dei discorsi sulla Session, alla fine lo spunto più interessante sia la diagnostica per arrivare alla soluzione del problema :)
05/07/2011 14:56 | Raffaele Rialdi
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Poco male Andrea e grazie ugualmente.
Prima di pensare a cose esoteriche ho preferito chiederti se ti veniva in mente qualcosa ma alla fine è bello che seguendo la procedura di diagnostica "classica" si ottiene sempre qualcosa :)
06/07/2011 19:46 | Raffaele Rialdi
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

complimenti, bell'articolo
07/07/2011 16:36 | Abx
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Grazie :)
08/07/2011 00:26 | Raffaele Rialdi
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Alessandro, i tempi diventano sostanzialmente quelli delle immagini statiche, quindi trascurabili.
Occhio che le richieste del browser avvengono in modo multithreading, quindi il tempo totale non è la somma dei tempi che vedi nel profiler.
Ad ogni modo quando la reidratazione della session è spropositamente superiore al task che stai eseguendo, ovviamente è solo d'impiccio.
08/07/2011 12:08 | Raffaele Rialdi
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

@Matteo. Sulla session ovviamente non devi mai fare affidamento. Il concetto per cui è nata è: se c'è la usi, altrimenti devi essere in grado di ricostruire i dati che c'erano dentro (o un default).
Va da se che per una persistenza a lungo termine la session non ha alcun senso, a quello scopo meglio le profile api (che funzionano egregiamente anche per il guest).

Se la disabiliti globalmente, se non ricordo male, perdi anche il SessionID, che è decisamente utile perché ti apre la possibilità di avere una session basata sull'oggetto Cache.

Io la uso per evitare il 'cheating' sul rating degli articoli (umano o di un bot).

Nel caso specifico che ho mostrato il db lo escludo se no quei tempi diventano biblici e poi non capisco che c'entri il discorso dell'integrità... semmai di persistenza che puoi garantire in tanti modi.
08/07/2011 12:27 | Raffaele Rialdi
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Più semplice no :) Il session id è lì bello e pronto che non aspetta altro di essere usato.
Di per se la session non è un killer, ovviamente se per una pagina che contiene 20 immagini la reidrati 20 volte, magari c'è qualcosa che non va ;-)
08/07/2011 13:04 | Raffaele Rialdi
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Povera session :-)
Io invece la uso anche per metterci dentro il viewstate per pagine che per forza hanno il viewstate cicciotto...
08/07/2011 15:04 | AlessandroD
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

:)
Per certe applicazioni webform ha sicuramente molto senso.
Per applicazioni moderne dove hai bisogno di avere sotto controllo il codice lato client, ha più senso MVC (poi ovviamente ci sono molte altre motivazioni) dove il viewstate non c'è più.
08/07/2011 16:58 | Raffaele Rialdi
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Bellissimo articolo!
21/07/2011 14:44 | Carrarini Daniel
Gravatar

# re: Asp.net MVC, immagini dinamiche e performance

Grazie! :)
21/07/2011 18:39 | Raffaele Rialdi
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET