2010-06-30 1 views
10

Ultimamente, mi sono trovato in una serie di discussioni con il mio capo sulla gestione delle eccezioni all'interno della nostra app Web (un'applicazione ASP # MVC di asp.net).Quanto resiliente dovrebbe essere la mia app web?

Fondamentalmente le conversazioni più o meno così:

Boss: "C'è qualcosa che non va con il nostro programma, database clienti di x è andato giù oggi e tutti sono di vedere la pagina di errore."

Me: "Per lo più ogni pagina dell'applicazione utilizza il database per qualcosa (eccetto la pagina di errore), non c'è altra alternativa ragionevole che mostrare la pagina di errore."

Boss: "La nostra applicazione dovrebbe essere più resiliente - la parte dell'applicazione che non richiede l'accesso al database dovrebbe comunque funzionare."

Spesso, i casi sono estremi come questo, ma a volte ci imbattiamo in un caso in cui ci stiamo integrando con un altro servizio in cui possiamo ancora mostrare in modo sicuro altre porzioni della pagina, o completare l'operazione, anche se con qualche codice fastidioso poiché le parti successive del codice devono utilizzare in seguito i risultati dell'operazione che potrebbe aver avuto esito negativo. Se ci sono molti punti di possibile fallimento questo può trasformarsi in un codice estremamente ingestibile.

In generale, per un'applicazione Web "normale" (non mission-critical, ecc.) Quanto tempo impiegano gli "bravi" sviluppatori a cercare di rendere il proprio codice sufficientemente flessibile per gestire questo tipo di situazioni. Il mio capo sembra pensare che il codice dovrebbe essere in grado di gestire quasi tutte le situazioni (non è possibile rilevare un'eccezione?). Non vedo come questo possa essere economico quando ci sono molti possibili punti di errore.

+2

non dovrebbe essere un wiki? – AGoodDisplayName

+0

In questa economia, fai quello che ti dirà il capo. –

+0

L'esempio del database è forse un po 'estremo e semplificato. Fondamentalmente abbiamo alcune pagine che interagiscono con più servizi di terze parti: geocoding/facebook/etc ... Potrebbero esserci diverse chiamate a un determinato servizio eseguite nell'esecuzione di una pagina. I risultati di un servizio possono essere successivamente passati a un altro o utilizzati nel rendering della pagina. Supponendo che uno di questi possa essere inattivo e cercare di produrre risultati sensibili in ogni caso sembra diventare molto difficile in presenza di molti di questi servizi. – Krazzy

risposta

12

Lo lascerei al capo per decidere. Digli una stima in poche ore del tempo necessario per rendere l'app "resiliente" e deciderà se vale la pena investire.

+0

Questo è, in effetti, l'approccio che di solito prendo. Di solito è abbastanza efficace anche se a volte percepisco il sentimento che le mie stime sono esagerate e che dovrebbe essere una questione di "solo prendere un'eccezione" che raramente è. – Krazzy

2

Dipende ... molte pagine Web visualizzano e rendono singoli "widget". Ogni widget può trarre da una diversa fonte di dati ... alcuni potrebbero addirittura essere completamente statici. Sono d'accordo con il tuo capo dal punto di vista che se il caricamento di un widget fallisce per qualsiasi motivo, dovremmo comunque provare a caricare il resto della pagina. Un widget non riuscito non dovrebbe impedire all'utente di interagire con il resto del sito.

Come esempio da StackOverflow, è possibile affermare che la sezione "Correlata" a destra di queste risposte non viene caricata o che il footer non riesce a caricare per qualche motivo. Direi che è preferibile visualizzare solo alcuni messaggi di errore in quelle parti della pagina e caricare ancora la tua domanda e le risposte, poiché queste dovrebbero essere inalterate.

Ora, ci sono situazioni in cui il codice che carica tutti quei "widget" non riesce ... o qualche origine dati utilizzata dal tuo framework fallisce. In questi casi, penso che sia ragionevole visualizzare una pagina di errore generica ... Se il framework o il "caricamento" del codice di caricamento fallisce per qualsiasi ragione, questo è davvero un caso eccezionale che probabilmente non può essere gestito.

In sintesi, per lo più sono d'accordo con il tuo capo e dico che tutti i possibili errori dovrebbero essere considerati e gestiti ... e che dovremmo mostrare tutto ciò che è possibile all'utente.

3

Immagino che sarei un po 'preoccupato che stai perdendo database con una frequenza che fa sorgere questa domanda. Anche nelle app non mission-critical, se stai perdendo un DB più di una volta alla settimana, vedrei cosa potrei fare per migliorarlo prima che mi preoccupassi di creare vie d'uscita dal problema all'utente finale dell'applicazione .

Detto questo, le best practice della mia azienda includono la codifica in modo che qualcosa come un errore del DB esegua il failover con garbo sul terminale dell'utente a un messaggio "non può connettersi" nell'output piuttosto che un errore di tipo 404 completo. Ho scoperto che in realtà non aggiunge più di qualche minuto al processo di codifica, e il valore di non far arrabbiare l'utente vale il "costo"

+0

Forse l'esempio del database non è stato buono perché è un po 'estremo. Di solito è una questione di integrazione con servizi web di terzi e simili. – Krazzy

7

La maggior parte delle app su cui ho lavorato sono i dati fortemente pilotati sono stati reindirizzati a una pagina di errore personalizzata quando il database principale (ovvero quello che alimenta il 99% delle pagine) è inattivo. (si vuole mostrare loro una schermata personalizzata, non solo lasciare che ottengano la pagina di errore del server)

Per servizi esterni come colpire un server SMTP o un database che non viene utilizzato da gran parte del resto del l'app di solito contiene un codice che mostra solo feedback in-page se il servizio/database è inattivo/inaccessibile.

Tuttavia, è davvero in cima al cliente/stakeholder, basta determinare cosa vogliono che si verifichi quando il database è inattivo e farlo per loro. Ci vorrà del tempo ma non dovrebbe portare a un'app non mantenibile o ad altri horror di programmazione.

3

Il tuo capo dovrebbe aver discusso con il cliente, prima di creare l'applicazione. Termini come "non critico" dovrebbero essere definiti come un numero (percentuale di tempo di attività). Alcune applicazioni richiedono parti diverse dell'applicazione per avere diversi numeri di tempo di attività (cosa suggerisce il tuo capo) e le applicazioni sono su/giù nel suo complesso (come funziona ora). L'applicazione "resiliente" verrà probabilmente scritta in un modo diverso (letture distribuite/scritture asincrone, ecc.) Rispetto all'applicazione "normale", quindi sarà (probabilmente) difficile convertire l'applicazione "normale".

Un buon capo discute un buon SLA con il cliente dell'applicazione e lo comunica agli sviluppatori prima dell'inizio dello sviluppo. Dall'altra parte, un buon sviluppatore dovrebbe lamentarsi dal suo capo, sui requisiti incompleti prima di iniziare lo sviluppo. Quando non viene menzionato nulla sulla disponibilità nei requisiti, i requisiti sono incompleti.

Quando i requisiti di disponibilità o scalabilità di un'applicazione esistente cambiano in modo significativo, potrebbe essere molto difficile rendere proficue le modifiche in un'applicazione. Quando hai un buon capo, questi requisiti cambieranno significativamente solo quando l'applicazione avrà molto più successo di quanto inizialmente stimato (come 100 volte più utenti). In questo caso l'enorme successo genererà abbastanza denaro per rendere redditizio riscrivere parti enormi dell'applicazione.

2

Questa è probabilmente una risposta più accademica dato che hai ereditato questo codice. Se si utilizza, o piuttosto se qualcuno ha utilizzato, il pattern Microsoft.Practices.EnterpriseLibrary.ExceptionHandling ExceptionPolicy, allora è davvero facile passare dalla visualizzazione di una pagina di errore (generando eccezioni) a mangiare eccezioni e visualizzare griglie vuote, liste, ecc.

si può già essere a conoscenza di questo piccolo modello, ma qui è in ogni caso:

 try 
     { 
      //get data 
     } 
     catch (Exception ex) 
     { 
      if (ExceptionPolicy.HandleException(ex, "Data Access Exception")) 
       throw ex; 
     } 
1

Probabilmente si sta solo mostrando una pagina di errore generico, quando un insieme vuoto di dati con e "user-friendly "messaggio di errore farebbe.

Quindi, ad esempio, se si sta visualizzando un elenco di utenti/messaggi/data ma il servizio in cui lo si raccoglie, è inattivo, è probabile che si possa visualizzare un set vuoto.

Così, invece di mostrare:

500 Server Error 




. 

Si potrebbe mostrare qualcosa di simile:

User | Message  | Date 
------------------------------ 
    No data available* 




* Xyz service is be down. 

La vostra applicazione ancora non funziona, perché i dati non sono disponibili, ma invece di buttare che a il volto dell'utente finale, potresti mettere un segnaposto senza dati.

questo varia molto a seconda di quello che si utilizza, ma in generale potrebbe essere semplice come:

List<Data> data = EmptyList<Data>(); 

try 
{ 
    data = service.GetData(); 
} catch(ServiceUnavailableException error) 
{ 
    errorPage.SetMessage(service.GetName() + " service is down "); 
    // log the error message 
    logger.doLog(error); 
} 

Cioè, inizializzare la vostra lista o qualunque sia la struttura di qualcosa di vuoto, poi riempire con la servizio, e se fallisce (quindi l'elenco rimarrà vuoto) aggiungere un messaggio di errore (user friendly per favore) e registrare l'eccezione.

-1

Penso che una volta che iniziamo a praticare qualcosa, costruendo applicazioni resilienti per esempio, dedichiamo poco tempo alla categorizzazione come normale o mission critical e iniziamo a creare applicazioni resilienti, ha, scalabili ecc. Per gestire i guasti.

Ulteriori sforzi sono sicuramente necessari per garantire applicazioni resilienti, tuttavia una volta acquisita e implementata la conoscenza, La prossima volta diventa molto più semplice. Sono già disponibili librerie generiche come la primavera o l'interruttore di rete di Netflix che, in fase di integrazione, possono gestire i guasti con garbo in alcuni aspetti. Anche ritentare un'operazione idempotente è facile da implementare.

Per rendere un'applicazione utilizzabile, deve essere disponibile e affidabile. Quasi tutte le applicazioni (diverse dalle utilità desktop autonome) sono 2 o più applicazioni a più livelli, il che significa che ci saranno chiamate di rete e tutti i problemi relativi a n/w. Quindi, una volta iniziato a praticare, inizierai ad implementare modelli di resilienza in tutte le applicazioni.

+0

La domanda in sé è (ora) fuori tema (notare che è stata pubblicata più di 7 anni fa). Anche la risposta accettata è solo un'opinione. Si prega di non aggiungere a questo con più opinione (il vostro essere della varietà n-tier, e non considerando la moderna architettura dei microservizi). Inoltre, ho rimosso il link al tuo blog - questo non è il posto giusto. –