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,Object src) at Oracle.DataAccess.Client.OracleConnection.Open() at MyWorkspace.WorkForceDataAccess.CheckStaffIdInRSW() at MyWorkspace.MyClass.MyFunction(MyDataType MyData)
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()); }
我非常确信正在调用的存储过程不会占用所有时间.这是一个非常简单的过程,可以快速检查表中是否存在P_Staff_Id并返回结果.
此外,这仅在负载测试期间发生.在正常操作期间,事情很好,但在重载期间每秒有1条消息,这会在平稳运行一段时间后发生.
作为一种解决方法,我在连接字符串中添加了“连接超时= 600;最大池大小= 150”,但这并没有解决问题.
我们在开发服务器上运行相同的应用程序,它工作正常.我们从未遇到过这个问题.
任何关于尝试什么的建议将不胜感激.看起来我的选项已经用完了.
解决方法
我修改了DbContext构造函数以显式打开OracleConnection.我添加了一些像这样的代码
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() }
它有所改进,但仍未完全解决.我从调试器中打破了捕获,并看到许多线程正在尝试打开并且很少有线程正在处理.
在Oracle堆栈中打开时,Oracle的内部用途是ThreadPool.QueueUserWorkItem并等待其完成.我可以在堆栈上看到它的等待.这里有大量的池连接(默认为100),我几乎不使用10.所以它不是资源.
但问题出在我们的代码中,我们还使用了ThreadPool.QueueUserWorkItem而没有额外的限制.我认为排队我们需要做的所有工作,我们需要多少工作,让.NET处理这个问题很酷.但这有一个微妙的问题.我们所有的工作都消耗了完整的队列数.当OracleConnection想要从池中获取池连接时,它也会排队到线程池.但它永远不会完成.我们的所有工作都在等待OracleConnection.Open,它的Queued Thread proc仍然在队列中.所以最后等待将在超时时退出.
遗憾的是,即使有大量的池连接可用,我们已经消耗了所有ThreadPool proc,而Oracle的线程池甚至没有机会.这里设置ThreadPool.SetMaxThreads也无济于事.问题仍然存在.我们占用了所有线程池资源,Orcale不会找到一个并且仍然在队列中.
修复不仅仅依赖于ThreadPool,而且还添加了我们自己的限制.我使用了BlockingCollection和sempahores,并在ThreadPool中添加了一些限制数量的并发作业,比如说5.这样OracleConnection总会找到一个可用的ThreadPool线程,并且不会失败.