Oracle连接重试需精准识别瞬时错误(如ORA-12541、ORA-12170),避开永久性错误(如ORA-01017);必须包裹“新建连接+Open”全过程,禁用连接池(Pooling=false),使用异步指数退避策略,并避免在事务中重试。

Oracle连接失败时,Polly重试策略必须避开连接字符串错误
直接套用通用重试逻辑往往失败——OracleConnection.Open() 抛出的异常类型和触发时机很关键。不是所有 OracleException 都该重试,比如用户名密码错误(ORA-01017)或 TNS 配置错误(ORA-12154)属于永久性失败,重试只会延长故障感知时间。
实操建议:
- 只对网络瞬断类错误重试:重点关注
ORA-12541(TNS 无监听)、ORA-12170(连接超时)、ORA-03113(通信中断)等;可通过OracleException.Number精确判断 - 避免在
Open()前就创建连接对象——Polly 应包裹整个“新建连接 + Open”过程,否则连接对象可能已处于Broken状态,重试时调用Open()仍会失败 - 不要用
Policy.Handle<Exception>()宽泛捕获,它会把ArgumentNullException或配置解析异常也纳入重试,导致逻辑混乱
使用 AsyncRetryPolicy 包裹 OracleConnection.OpenAsync()
.NET Core 下必须用异步重试策略,同步 RetryPolicy 会阻塞线程池,尤其在高并发 Oracle 查询场景下容易引发线程饥饿。重试间隔推荐采用指数退避(WaitAndRetryAsync),而非固定延迟。
示例关键片段:
var retryPolicy = Policy
.Handle<OracleException>(ex => IsTransientOracleError(ex.Number))
.Or<TimeoutException>()
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryCount, context) =>
{
Log.Warning("Oracle connection attempt {RetryCount} failed: {Exception}. Retrying in {Delay}ms",
retryCount, outcome.Exception?.Message, timespan.TotalMilliseconds);
});
注意:IsTransientOracleError 是你自定义的判断函数,返回 true 仅当 ex.Number 属于可重试列表。
连接字符串中禁用连接池(Pooling=false)才能让每次重试真正新建物理连接
Oracle 默认开启连接池,这意味着即使 OpenAsync() 失败,Polly 重试时仍可能从池中取出一个已失效的连接句柄,导致重试无效。这不是 Polly 的问题,而是 Oracle 数据提供程序的行为。
必须显式关闭连接池:
- 在连接字符串末尾添加
;Pooling=false - 若使用
OracleConfiguration全局设置,需确保它不被其他组件覆盖(例如 Entity Framework Core 可能忽略该设置) - 关闭池后性能略有下降,但这是换取可靠重试的必要代价;生产环境若需兼顾性能,应配合连接健康检查(如定期
PingAsync())而非依赖重试兜底
EF Core 场景下不能直接对 DbContext 构造函数应用 Polly
EF Core 的 DbContext 生命周期由 DI 容器管理,构造函数里抛异常会导致整个作用域失效。Polly 必须下沉到具体操作层,比如 SaveChangesAsync() 或原始 SQL 执行点。
正确做法:
- 对
context.Database.GetDbConnection().OpenAsync()单独加 Polly(适用于手动控制连接的场景) - 更常见的是封装仓储方法,例如
IRepository<T>.FindAsync()内部先确保连接打开,并在此处应用重试 - 避免给
ExecuteSqlRawAsync()这类方法加全局重试——它可能执行 DDL 或非幂等语句,重复执行会破坏数据一致性
最易被忽略的一点:Oracle 的连接重试必须与事务边界严格对齐。如果已在事务中(BeginTransaction 后),连接中断再重试并重新 Open,新连接无法自动加入原事务——此时应直接放弃重试,让上层处理回滚逻辑。


















