2

背景

我们有一个 C#/VB.net 客户端应用程序使用连接到 Oracle 数据库的 WCF 服务。Web 服务使用 .NET 框架的 Oracle 数据提供程序连接到数据库(不要与 ODP 混淆)。我们的测试人员经历了偶发的 Oracle 帐户锁定,这似乎是在更改用户的 Oracle 密码后不久发生的。dba_audit_trail 日志显示了在没有任何用户或客户端活动的情况下,似乎以非常固定的时间间隔(即每两分钟一次)自动连接尝试。许多日志(IIS、WCF 跟踪、消息日志等)已确认这些连接尝试不是由客户端应用程序直接发起的;它们必须独立于 Web 服务或 System.Data.OracleClient 库内部。

在某些情况下,这些自动尝试在密码更改之前开始,并且它们成功连接到数据库,但是一旦密码更改,下一次尝试会因用户名/密码无效而失败。尝试三次后,帐户被锁定。我们正试图找到这些周期性连接尝试的来源。

我在此处的 Oracle 论坛上发现了一个非常相似但未得到解答的问题。

当前的想法

我们最近的调查使我们相信这是连接池的意外行为。如果用户在密码更改之前连接到 Web 服务,则会为原始连接字符串创建一个连接池。更改密码并重新登录 Web 服务后,数据提供者将根据新的连接字符串创建一个新的连接池。

数据提供者内部的某些东西是否会试图使旧连接与第一个连接池保持活动状态?也许第一个连接池正在丢弃旧连接并尝试用新连接(现在无效的连接字符串)来补充它。什么可能导致这种情况?注意:我们使用最小/最大池大小 (0/100) 的默认设置。

我们不相信我们的代码直接尝试从第一个连接池访问连接。用户的会话没有任何关于前一个会话密码的记忆,因此不会使用旧的连接字符串来引用第一个连接池。此外,我们的代码中没有任何内容可以解释我们所看到的非常精确的连接间隔。

4

3 回答 3

3

根本问题最终是未发布的数据库连接。打开连接时,它会从连接池中检出。如果连接从未关闭,则池认为它仍在使用中。这会导致池管理逻辑使用原始连接字符串定期重新验证数据库。当密码更改时,这很快会导致登录尝试失败和帐户锁定。

// Problem logic; connection is never closed/returned to the connection pool.
public static void ConnPoolTest1()
{
    OracleConnection conn = new OracleConnection(connectionStringWithPooling);
    conn.Open();

    //...Do some work

    // Sit on this line for 5-10 minutes and examine Oracle's dba_audit_trail.
    Console.ReadKey(); // Since connection was never released back to the connection pool, the
                       // data provider's pool management will regularly re-authenticate with DB.
                       // If user's password changes before this process dies (releasing the
                       // connection pools), you start accumulating failed password attempts.
}

解决此问题的正确方法是确保在完成连接后始终将连接返回到池中!

// Best practice: ALWAYS CLOSE YOUR CONNECTIONS WHEN YOU ARE DONE!
public static void ConnPoolTest2()
{
    OracleConnection conn = new OracleConnection(connectionStringWithPooling);
    conn.Open();

    //...Do some work

    conn.Close();

    // Sit on this line for 5-10 minutes and examine Oracle's dba_audit_trail.
    Console.ReadKey(); // No problem here! No recurring authentication attempts because the
                       // connection has been returned to the pool.
}

注意:其他答案建议在密码更改时关闭池并清除旧连接池。这些建议在我们搜索未发布资源时作为临时补丁为我们工作,它们极大地帮助我们隔离了问题。

于 2012-10-15T21:50:41.033 回答
2

这可能会有所帮助。

Oracle ODP.Net 和连接池

OLE DB、ODBC 和 Oracle 连接池

基本上,在那里的第二个网页中,MSDN 声明“一旦创建,连接池在活动进程结束之前不会被破坏。”。看起来您的 Web 服务可能会占用如此多的连接/连接池,以至于它遇到了一些问题。

所以我的建议:除了做更多的故障排除可能会添加一些连接日志(可能只是一个文本文件),或者第一个链接有一个很好的命令来跟踪与数据库的连接,我会尝试关闭连接池目前。您似乎遇到的问题称为“池碎片”。这是您通过连接池传输所有数据库连接的单台计算机的流量很大的地方。最终会出现如此多的池,以至于开始出现内存问题并且连接没有正确关闭。第二个将是您的问题,如果连接未关闭,或者假设您的密码更改命令在使用旧连接池的其他命令列表之前执行,您将会遇到问题。

最终,在您的情况下,您将拥有一个单点(Web 服务),它创建自己的 Web 连接池(不是为用户)并通过自己的连接将数据提供给用户。这意味着必须有不同类型的身份验证,由 Web 服务端处理,以处理连接的用户。我敢肯定,现在您的模型可能会有太多变化,但我强烈建议您最终寻找解决方案。

于 2012-10-14T13:10:10.107 回答
2

每当发生任何会使连接无效的事件时,您都需要销毁池,以便适当地标记池的任何泄漏连接和/或保持活动状态以防止重复使用。为此,您需要使用数据提供者的clearpoolorclearallpools方法。

http://msdn.microsoft.com/en-us/library/system.data.oracleclient.oracleconnection.clearpool.aspx

此外,您的全局异常管理器可以侦听为无效用户引发的异常,并通过枚举标识为连接字符串一部分的用户的连接来销毁该池。可能效率不高,但应该完成工作。

于 2012-10-15T06:23:17.593 回答