背景
我们有一个C#/ VB.net客户端应用程序,它使用连接到Oracle数据库的WCF服务。 Web服务使用.NET框架的Oracle数据提供程序连接到数据库(不要与ODP混淆)。我们的测试人员经历了零星的Oracle帐户锁定,这似乎是在更改用户的Oracle密码后不久发生的。 dba_audit_trail日志显示了似乎是非常规则间隔的自动连接尝试(即点上每两分钟)没有任何用户或客户端活动。许多日志(IIS,WCF跟踪,消息记录等)已确认这些连接尝试不是由客户端应用程序直接启动的;它们必须独立于Web服务或System.Data.OracleClient库内部。自动尝试将一直持续到Web服务的工作进程(单个工作程序)因不活动而死亡。
在某些情况下,这些自动尝试在密码更改之前启动,并且它们成功连接到数据库,但是一旦密码更改,下一次尝试就会因无效的用户名/密码而失败。三次尝试后,帐户被锁定。我们正在尝试找到这些定期连接尝试的来源。
我在Oracle论坛here上发现了一个非常相似但没有答案的问题。
当前的想法
我们最新的调查使我们相信这是连接池的意外行为。如果用户在更改密码之前连接到Web服务,则将为原始连接字符串创建连接池。更改密码并重新登录到Web服务后,数据提供程序将根据新的连接字符串创建新的连接池。
数据提供程序中的某些内容是否可以尝试保持旧连接从第一个连接池保持活动状态?也许第一个连接池正在丢弃旧连接并尝试用新连接补充它(使用现在无效的连接字符串)。什么可能导致这个?注意:我们使用最小/最大池大小(0/100)的默认设置。
我们认为我们的代码不会直接尝试从第一个连接池访问连接。用户的会话没有上一个会话密码的任何内存,因此不会使用旧的连接字符串来引用第一个连接池。此外,我们的代码中没有任何内容可以解释我们看到的非常精确的连接间隔。
答案 0 :(得分: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.
}
注意:其他答案建议在密码更改时关闭池并清除旧连接池。当我们搜索未发布的资源时,这些建议对我们来说是一个临时的补丁,它们极大地帮助我们解决了这个问题。
答案 1 :(得分:2)
这可能会有所帮助。
Oracle ODP.Net and connection pooling
和
OLE DB, ODBC, and Oracle Connection Pooling
基本上,在那里的第二个网页中,MSDN声明“一旦创建,连接池就不会被销毁,直到活动进程结束。”您的Web服务似乎可能会持有这么多连接/连接池,因此它存在一些问题。
所以我的建议:除了做一些额外的连接日志记录(可能只是一个文本文件),或者第一个链接有一个很好的命令来跟踪数据库的连接,我会尝试关闭现在连接池。您似乎遇到的问题称为“池碎片”。这是您通过连接池管理所有数据库连接的单台计算机的流量很大的地方。最终会有这么多池,内存问题开始发生,连接没有正确关闭。第二个是你的问题,如果没有关闭连接,或者让你说密码更改命令在使用旧连接池的其他命令列表之前执行,那么你将遇到问题。
最终,在您的情况下,您将拥有一个单点(Web服务)来创建自己的Web连接池(而不是用户),并通过自己的连接将数据提供给用户。这意味着必须有不同类型的身份验证,由Web服务端处理,以处理用户连接。我相信你的模型现在可能会有太大的变化,但我强烈建议最终找到解决方案。
答案 2 :(得分:2)
每当发生任何使连接失效的事件时,您都需要销毁池,以便适当标记池的任何泄漏连接和/或保持连接,以防止重复使用。为此,您需要使用数据提供程序的clearpool
或clearallpools
方法。
http://msdn.microsoft.com/en-us/library/system.data.oracleclient.oracleconnection.clearpool.aspx
此外,您的全局异常管理器可以侦听为无效用户抛出的异常,并通过枚举标识为连接字符串一部分的用户的连接来销毁该池。可能效率不高,但应该完成工作。