Oracle Data Provider for .NET:连接请求超时

Posted

技术标签:

【中文标题】Oracle Data Provider for .NET:连接请求超时【英文标题】:Oracle Data Provider for .NET: Connection request timed out 【发布时间】:2015-12-24 19:17:22 【问题描述】:

我们有一个托管在 Windows 2008 SP2/IIS 7 上的 C# WCF Web 服务,用于访问 Oracle 数据库。通常数据访问工作正常,但在负载测试期间,它经常超时并且日志和异常说:

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)

要查询数据库,我们使用如下代码:

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 条消息的重负载期间,这会在平稳运行一段时间后发生。

作为一种解决方法,我在连接字符串中添加了“Connection Timeout=600; Max Pool Size=150”,但这并没有解决问题。

我们在开发服务器上运行相同的应用程序,它运行良好。我们在那里从未遇到过这个问题。

任何关于尝试什么的建议都将不胜感激。看来我的选择已经不多了。

【问题讨论】:

堆栈跟踪表明程序不是问题。该异常在 Connection.Open 中执行之前引发,因此看起来数据库机器已超载,因此无法在超时时间内响应客户端。它不应该与 Oracle 中的池大小或进程限制有关,这些会引发不同的异常。此外,我会怀疑该池的大小,因为池明显大于数据库可以使用的核心数量是没有意义的。或者你在某处有连接泄漏。 在出现此问题后,我在连接字符串中添加了连接超时和最大池大小 - 但没有帮助。没有这些,Web 服务在 DEV 环境中运行良好。通过连接泄漏,您是否建议在使用 OracleConnection 对象后显式关闭和处置它? 广告连接泄漏 - 如果连接对象在单个函数中的寿命很短,那么使用 (var connection = ...) ... 肯定更安全。但我不认为这是问题所在。当池被完全使用时,你会得到不同的异常。广告负载测试 - 我希望您并行运行应用程序或功能的许多实例。还期望您使用专用连接,而不是共享服务器作为 Oracle 设置。您能否在测试期间检查会话在数据库中的外观,以了解实际有多少会话和活动会话。 【参考方案1】:

我们遇到了类似的问题,调试和修复它需要一段时间。我们的代码对许多输入文件和许多线程处理感到压力,每个线程使用实体框架并打开 Oracle 数据库连接,并执行一堆数据库查询和插入,用于偶尔归档。但大部分时间都有效。

我修改了 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 仍然在队列中。所以最后等待将通过超时退出。 遗憾的是,即使有大量的pooled connection可用,我们已经消耗了所有的ThreadPool proc,而Oracle的threadpool甚至没有机会。在这里设置 ThreadPool.SetMaxThreads 也无济于事。问题还是一样的。我们占用了所有线程池资源,而 Orcale 不会找到并且仍然在队列中。

解决方法不是仅依赖 ThreadPool,而是我们也添加了自己的节流。我使用了 BlockingCollection 和 sempahores,并在 ThreadPool 中仅添加了一些限制数量的并发作业,例如 5。这样,OracleConnection 将始终找到可用的 ThreadPool 线程,并且不会失败。

【讨论】:

非常感谢这个答案!对创建连接的内部机制的出色洞察力。我面临同样的问题,很长一段时间以来我都不清楚问题的原因是不是数据库代码,而是应用程序的不同部分!【参考方案2】:

即使我曾经更频繁地遇到此问题,即使在使用 Connection.Close() 之后也是如此

经过长时间的分析,我学到了一些东西,如下所述

    Connection.Close() 不处理数据库连接 连接超时并不意味着问题仅与数据库查询有关 连接超时也可能是由于连接池中的连接过多(这是我的罪魁祸首,因为它达到了数据库连接的最大会话数)

修复:- 分析花了很长时间,但修复只需 2 分钟

using(DbConnection instance)



例如:-

using (DbConnection  objDbConnection = new DbConnection())

   ojDbConnection.PersistData();

在 PersistData() 下;所有的数据库操作,如打开、关闭等。将执行

众所周知,“使用”是的缩写形式

try



catch()



Finally

  Dispose objDbConnection;

希望它有所帮助,因为它帮助了我

【讨论】:

【参考方案3】:

尝试在最后添加 connection.close() 。 我没有看到在您的代码中释放连接并将它们显式返回到连接池。 所以只有在 GC 启动时才会将连接返回到连接池。

【讨论】:

我们实现了类似这样的连接。 close() 显式关闭连接,这似乎有效。谢谢大家的帮助。

以上是关于Oracle Data Provider for .NET:连接请求超时的主要内容,如果未能解决你的问题,请参考以下文章

Microsoft OLEDB Provider for Oracle 找不到,怎么解决

Couldn‘t find meta-data for provider with authority com.wust.camerademo

Cannot create an instance of OLE DB provider “OraOLEDB.Oracle” for linked server "xxxxxxx"

NgSwitch - 行为出乎意料,`No provider for NgSwitch` - 出现异常

[译] Data Guard:Oracle Database 21c 中的 PREPARE DATABASE FOR DATA GUARD 命令

Oracle数据库连接中Provider=OraOleDb.Oracle.1与Provider=MSDAORA啥区别?