2015-09-27 17 views
5

Abbiamo un servizio Web WCF C# ospitato su Windows 2008 SP2/IIS 7 che accede a un database Oracle. Di solito l'accesso ai dati funziona bene ma durante le prove di carico, è spesso fuori e registri e un'eccezione dicendo:Provider di dati Oracle per .NET: timeout della richiesta di connessione

Error occurred when processing XXXXXXXX Web Service 
Oracle.DataAccess.Client.OracleException Connection request timed out at Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck) 
    at Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, Object src) 
    at Oracle.DataAccess.Client.OracleConnection.Open() 
    at MyWorkspace.WorkForceDataAccess.CheckStaffIdInRSW() 
    at MyWorkspace.MyClass.MyFunction(MyDataType MyData) 

per interrogare il database, usiamo qualcosa di simile:

OracleConnection orConn = new OracleConnection(); 
orConn.ConnectionString = "user id=xxx; password=xxx; Connection Timeout=600; Max Pool Size=150; data source= (DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = MYHOST.MYDOMAIN.com)(PORT = 1771)) (CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = MYSERVICE.MYDOMAIN.com)))"; 
orConn.Open(); 

using (var cmd = new OracleCommand("MY_UTIL.check_StaffIdInRSW", orConn) { CommandType = CommandType.StoredProcedure }) 
{ 
    cmd.Parameters.Add("P_Staff_Id", OracleDbType.Int32); 
    cmd.Parameters["P_Staff_Id"].Direction = ParameterDirection.Input; 
    cmd.Parameters["P_Staff_Id"].Value = Convert.ToInt32(MyDataObject.StaffId); 

    cmd.Parameters.Add("P_retvalue", OracleDbType.Int32); 
    cmd.Parameters["P_retvalue"].Direction = ParameterDirection.Output; 

    cmd.ExecuteNonQuery(); // Execute the function 

    //obtain result 
    returnVal = int.Parse(cmd.Parameters["P_retvalue"].Value.ToString()); 
} 

Sono abbastanza sicuro che la stored procedure che viene invocata non sta prendendo tutto il tempo. È una procedura piuttosto semplice che controlla rapidamente se P_Staff_Id esiste nella tabella e restituisce il risultato.

Inoltre, ciò si verifica solo durante il test di carico. Durante le normali operazioni le cose vanno bene, ma durante carichi pesanti con 1 messaggio al secondo, questo si verifica dopo aver funzionato per un po 'di tempo.

Per aggirare il problema, ho aggiunto "Timeout connessione = 600; Max Pool Size = 150“ per la stringa di connessione, ma che non ha risolto il problema

Abbiamo la stessa applicazione in esecuzione su un server di sviluppo. e funziona benissimo. non abbiamo mai incontrato questo problema.

eventuali suggerimenti riguardo a che cosa da provare sarebbe apprezzato. sembra che io sono a corto di opzioni.

+0

Lo StackTrace suggeriscono che la procedura non è il problema. L'eccezione viene sollevata prima dell'esecuzione in Connection.Open, quindi sembra che il computer del database sia sovraccarico e non possa rispondere al client entro il periodo di timeout. Non dovrebbe essere correlato alla dimensione del pool o ai limiti dei processi in Oracle, questi generano eccezioni diverse. Inoltre, sarei diffidente riguardo alle dimensioni del pool in quanto non avrebbe senso disporre di un pool notevolmente più grande del numero di core che il database può utilizzare. O hai una perdita di connessione da qualche parte. – Husqvik

+0

Ho aggiunto Timeout connessione e Dimensione massima pool alla stringa di connessione dopo che si è verificato questo problema, ma non è stato di aiuto. Il servizio web funzionava perfettamente nell'ambiente DEV senza questi. Per perdita di connessione, suggeriresti di chiudere e smaltire l'oggetto OracleConnection in modo esplicito dopo che è stato utilizzato? – DjD

+0

Perdita della connessione annuncio: se l'oggetto connessione ha solo un breve live nella singola funzione, utilizzare (var connection = ...) {...} è sicuramente più sicuro. Ma non mi aspetto che questo sia il problema. Si otterrebbe un'eccezione diversa quando il pool è completamente utilizzato. Test carico annunci - Mi aspetto che tu esegua molte istanze dell'applicazione o della funzione in parallelo. Si aspettano inoltre che si utilizzino connessioni dedicate, non server condivisi come impostazione Oracle. Puoi controllare come appaiono le sessioni nel database durante il test per vedere quante sessioni e quante sessioni attive ci sono effettivamente. – Husqvik

risposta

4

Abbiamo avuto un problema simile, e ci sono voluti un po 'di tempo per eseguire il debug di questo e risolvere il problema. Il nostro codice su stressato con molti file di input e molti thread di elaborazione, ogni minaccia d utilizzando Entity framework e aprendo la connessione db Oracle e facendo un sacco di query e inserimenti db, usati occasionalmente per archiviare. Ma funziona la maggior parte del tempo.

Ho modificato il costruttore DbContext per aprire esplicitamente OracleConnection. Ho aggiunto un codice come questo

for (i = 0; i < 5; i++) 
    try { 
     oracleConnection.Open(); 
    } catch (OracleException) { 
    Sleep for 15 ms and retry. 
    On last attempt I also do OracleConnection.ClearAllPools() 
    } 

È migliorato, ma ancora non lo ha risolto completamente. Ho interrotto la cattura del debugger e ho visto che molti thread stanno tentando di aprirsi e pochi thread si stanno elaborando. On Apri nello stack Oracle, Oracle per il suo scopo interno fa ThreadPool.QueueUserWorkItem e attende il suo completamento. Posso vedere in cima alla pila la sua attesa. Qui sono disponibili molte connessioni in pool (il valore predefinito è 100), non lo uso quasi 10. Quindi non è fuori dalla risorsa.

Ma il problema è nel nostro codice, inoltre abbiamo utilizzato ThreadPool.QueueUserWorkItem senza alcuna limitazione aggiuntiva. Ho pensato che fosse bello accodare tutti i lavori che dovevamo fare, quanto abbiamo bisogno di questo e lasciare che .NET si occupasse di questo. Ma questo ha un problema sottile. Tutti i nostri lavori hanno consumato il conteggio completo della coda. Quando OracleConnection desidera ottenere una connessione in pool dal pool, si accoda anche al pool di thread. Ma non verrà mai completato. I nostri lavori sono tutti in attesa di OracleConnection.Open, e il suo thread in coda proc sarà ancora in coda. Quindi finalmente l'attesa uscirà dal timeout. È un peccato che anche se è disponibile una grande quantità di connessioni in pool, abbiamo consumato tutto il proc di ThreadPool e il threadpool di Oracle non ha nemmeno avuto la possibilità. Anche qui l'impostazione di ThreadPool.SetMaxThreads non aiuta. Il problema è sempre lo stesso. Gestiamo tutte le risorse del pool di thread e Orcale non ne troverà una e sarà ancora in coda.

La correzione non si basa solo su ThreadPool, ma aggiungiamo anche il nostro throttling. Ho usato BlockingCollection e Sempahores e aggiungo solo un numero limite di lavori simultanei in ThreadPool, diciamo 5. In questo modo OracleConnection troverà sempre un thread ThreadPool disponibile e non fallirà.

1

provare ad aggiungere connection.close() alla fine. Non vedo il rilascio di connessioni nel codice e il loro invio al pool di connessioni in modo esplicito. Quindi la connessione viene restituita al pool di connessioni solo all'avvio di GC.

+0

Abbiamo implementato qualcosa come questa connessione. close() per chiudere esplicitamente le connessioni e sembra funzionare. Grazie a tutti per il vostro aiuto. – DjD

1

Anche ho usato per ottenere questo problema più spesso, anche dopo l'uso di connection.Close()

Dopo una lunga analisi che ho imparato qualche cosa come indicato di seguito

  1. connection.Close () pretende molto smaltire la connessione db
  2. connessione Time out non significa che il problema è solo con interrogazione db
  3. timeout di connessione potrebbe anche essere a causa di connessioni esaurienti nel pool di connessioni (che era il colpevole per me come ha raggiunto il massimo sessioni imum della connessione db)

Fix: - analisi ha preso molto tempo, ma correzione è di soli 2 minuti

using(DbConnection instance) 
{ 

} 

Ad esempio: -

using (DbConnection objDbConnection = new DbConnection()) 
{ 
    ojDbConnection.PersistData(); 
} 

Sotto PersistData(); Tutte le operazioni db come Apri, chiudi e.tc. saranno eseguiti

Come tutti sa "Uso" è un'abbreviazione di

try 
{ 

} 
catch() 
{ 

} 
Finally 
{ 
    Dispose objDbConnection; 
} 

Speranza che aiuta, come mi ha aiutato

Problemi correlati