当使用Elasticsearch.Net
或NEST
客户端类库发起Elasticsearch远程调用并抛出不能被IConnection
处理的异常时,此异常又会当作UnexpectedElasticsearchClientException
类型的异常被冒泡式地向上抛出到客户端,不管客户端是否配置了抛出异常的选项。
一个IConnection
需要知道哪些异常是可以恢复的,哪些是不能被恢复的,IConnection
的默认设置如下:
- 桌面端的CLR是基于
WebRequest
,它可以从WebException
中恢复 - .NET Core CLR是基于
HttpClient
,它可以从HttpRequestException
中恢复
其他异常会导致连接立即退出管道,以下将使用虚拟集群测试框架来演示这一点:
var audit = new Auditor(() => VirtualClusterWith // 设置10个节点的Elastisearch集群
.Nodes(10)
.ClientCalls(r => r.SucceedAlways())
.ClientCalls(r => r.OnPort(9201).FailAlways(new Exception("boom!"))) // 端口为9201上的节点始终会抛出异常
.StaticConnectionPool()
.Settings(s => s.DisablePing())
);
audit = await audit.TraceCall(
new ClientCall {
{ AuditEvent.HealthyResponse, 9200 }, // 第一次调用9200端口将返回一个正常的响应
}
);
audit = await audit.TraceUnexpectedException(
new ClientCall {
{ AuditEvent.BadResponse, 9201 }, // 第二次调用9201端口将返回失败的响应
},
(e) =>
{
e.FailureReason.Should().Be(PipelineFailure.Unexpected);
e.InnerException.Should().NotBeNull();
e.InnerException.Message.Should().Be("boom!");
}
);
有时,未知的异常会发生在连接管道的更深处。在这种情况下,将些未知的异常封装在一个UnexpectedElasticsearchClientException
,这样就可以完整记录异常在管道中发生位置的信息,而不会丢失。
在接下来的示例中,调用9200端口失败并引发了WebException
异常,然后该调用转到9201上重试,9201端口会抛出一个IConnection
的异常。最终,可以断言我们仍然可以看到整个请求的异常审计跟踪。测试代码如下:
var audit = new Auditor(() => VirtualClusterWith
.Nodes(10)
#if DOTNETCORE
.ClientCalls(r => r.OnPort(9200).FailAlways(new System.Net.Http.HttpRequestException("recover"))) // 调用9200端口时抛出HttpRequestException或者WebException异常
#else
.ClientCalls(r => r.OnPort(9200).FailAlways(new System.Net.WebException("recover")))
#endif
.ClientCalls(r => r.OnPort(9201).FailAlways(new Exception("boom!"))) // 调用9201端口时抛出Exception异常
.StaticConnectionPool()
.Settings(s => s.DisablePing())
);
audit = await audit.TraceUnexpectedException(
new ClientCall {
{ AuditEvent.BadResponse, 9200 },
{ AuditEvent.BadResponse, 9201 }, // 断言客户端调用的审计跟踪包括来自9200和9201的错误响应
},
(e) =>
{
e.FailureReason.Should().Be(PipelineFailure.Unexpected);
e.InnerException.Should().NotBeNull();
e.InnerException.Message.Should().Be("boom!");
}
);
如果在使用Ping
或者Sniff
时出现了未知的异常,Elasticsearch.Net
或者NEST
客户端将试图从中恢复,并将故障转移到下一个节点上重新尝试。
以下示例中,启用了在第一次使用时ping节点,端口9200上的节点在ping时抛出了异常,此时,将故障转移到端口9201上的节点上重试ping,此节点ping成功了。之后,对9201的客户端调用时抛出一个无法恢复的异常,代码如下:
var audit = new Auditor(() => VirtualClusterWith
.Nodes(10)
.Ping(r => r.OnPort(9200).FailAlways(new Exception("ping exception")))
.Ping(r => r.OnPort(9201).SucceedAlways())
.ClientCalls(r => r.OnPort(9201).FailAlways(new Exception("boom!")))
.StaticConnectionPool()
.AllDefaults()
);
audit = await audit.TraceUnexpectedException(
new ClientCall {
{ AuditEvent.PingFailure, 9200 },
{ AuditEvent.PingSuccess, 9201 },
{ AuditEvent.BadResponse, 9201 },
},
e =>
{
e.FailureReason.Should().Be(PipelineFailure.Unexpected);
e.InnerException.Should().NotBeNull();
e.InnerException.Message.Should().Be("boom!"); // InnerException 是导致请求中断的异常
e.SeenExceptions.Should().NotBeEmpty(); // 但是发生在Ping上的异常仍然可用
var pipelineException = e.SeenExceptions.First();
pipelineException.FailureReason.Should().Be(PipelineFailure.PingFailure);
pipelineException.InnerException.Message.Should().Be("ping exception");
var pingException = e.AuditTrail.First(a => a.Event == AuditEvent.PingFailure).Exception; // 异常可能很难关联到某个时间点,因此可以在异常的审计跟踪中找到异常
pingException.Should().NotBeNull();
pingException.Message.Should().Be("ping exception");
}
);
发表评论
登录用户才能发表评论, 请 登 录 或者 注册