不可恢复的异常

1109 发布于: 2021-03-24 读完约需 3 分钟

在使用Elasticsearch.NetNEST客户端时,遇到不可恢复的异常是一种需要立即退出客户端管道的预期异常。

默认情况下,客户端不会抛出任何ElasticsearchClientException,而是返回一个无效响应,.NET开发者可以通过检查这个响应上的.IsValid属性来检测响应结果。你也可以在ConnectionSettings中使用ThrowExceptions()来改变这种行为。

var failures = Enum.GetValues(typeof(PipelineFailure)).Cast<PipelineFailure>();
foreach (var failure in failures)
{
    switch (failure)
    {
        /** 以下的管道故障是可恢复的并且将执行重试 */
        case PipelineFailure.PingFailure:
        case PipelineFailure.BadRequest:
        case PipelineFailure.BadResponse:
            var recoverable = new PipelineException(failure);
            recoverable.Recoverable.Should().BeTrue(failure.GetStringValue());
            break;

        /** 以下的管道故障是不可恢复的并且将不会执行重试 */
        case PipelineFailure.BadAuthentication:
        case PipelineFailure.SniffFailure:
        case PipelineFailure.CouldNotStartSniffOnStartup:
        case PipelineFailure.MaxTimeoutReached:
        case PipelineFailure.MaxRetriesReached:
        case PipelineFailure.Unexpected:
        case PipelineFailure.NoNodesAttempted:
            var unrecoverable = new PipelineException(failure);
            unrecoverable.Recoverable.Should().BeFalse(failure.GetStringValue());
            break;
        default:
            throw new ArgumentOutOfRangeException(failure.GetStringValue());
    }
}

以下作为一个示例,同样使用虚拟集群测试框架来建立一个有10个节点的集群,它在ping的时候总是成功的,但在客户端调用使用401作为失败响应,代码如下:

var audit = new Auditor(() => VirtualClusterWith
    .Nodes(10)
    .Ping(r => r.SucceedAlways()) // Ping始终成功
    .ClientCalls(r => r.FailAlways(401)) // 客户端调用时返回401的身份认证失败响应
    .StaticConnectionPool()
    .AllDefaults()
);

现在,发起一个客户端请求,你将看到第一个审计事件是成功的Ping,随后是401身份验证响应导致的错误响应

示例代码如下:

audit = await audit.TraceElasticsearchException(
    new ClientCall {
        { AuditEvent.PingSuccess, 9200 }, 
        { AuditEvent.BadResponse, 9200 }, 
    },
    exception =>
    {
        exception.FailureReason
            .Should().Be(PipelineFailure.BadAuthentication); 
    }
);

当出现错误的身份验证响应时,客户端试图反序列化返回的响应体。

在某些设置中,你的请求可能运行在代理中,这时,你可能需要阻止客户端反序列化不符合JSON规则的数据。

在下面的例子中,响应中返回的数据是带有application/json内容类型但内容是HTML格式的响应数据。如果你不能修改或者调整代理中的数据但仍需要在客户端修复此问题。这里,我们可以通过在ConnectionSettings上调用SkipDeserializationForStatusCodes()来告诉客户端,如果响应的状态码是401,请不要反序列化这个响应结果。示例代码如下:

var audit = new Auditor(() => VirtualClusterWith
    .Nodes(10)
    .Ping(r => r.SucceedAlways())
    .ClientCalls(r => r.FailAlways(401).ReturnByteResponse(HtmlNginx401Response, "application/json")) 
    .StaticConnectionPool()
    .Settings(s => s.SkipDeserializationForStatusCodes(401))
);

audit = await audit.TraceElasticsearchException(
    new ClientCall {
        { AuditEvent.PingSuccess, 9200 },
        { AuditEvent.BadResponse, 9201 },
    },
    (e) =>
    {
        e.FailureReason.Should().Be(PipelineFailure.BadAuthentication);
        e.Response.HttpStatusCode.Should().Be(401);
        e.Response.ResponseBodyInBytes.Should().BeNull(); 
    }
);

在接下来的示例中,我们调用了ConnectionSettings配置的DisableDirectStreaming()方法,可以看到和上面示例相同的行为。但这一次,响应体字节在响应中被捕获并可以被审查。

还要注意的是,在这个例子中,401返回了正确的mime类型(text/html),因此客户端不会试图反序列化为json,我们不再需要设置SkipDeserializationForStatusCodes()。示例代码如下:

var audit = new Auditor(() => VirtualClusterWith
    .Nodes(10)
    .Ping(r => r.SucceedAlways())
    .ClientCalls(r => r.FailAlways(401).ReturnByteResponse(HtmlNginx401Response, "text/html"))
    .StaticConnectionPool()
    .Settings(s => s.DisableDirectStreaming())
);

audit = await audit.TraceElasticsearchException(
    new ClientCall {
        { AuditEvent.PingSuccess, 9200 },
        { AuditEvent.BadResponse, 9200 },
    },
    (e) =>
    {
        e.FailureReason.Should().Be(PipelineFailure.BadAuthentication);
        e.Response.HttpStatusCode.Should().Be(401);
        e.Response.ResponseBodyInBytes.Should().NotBeNull(); 
        var responseString = Encoding.UTF8.GetString(e.Response.ResponseBodyInBytes);
        responseString.Should().Contain("nginx/"); 
        e.DebugInformation.Should().Contain("nginx/");
    }
);

版权声明:本作品系原创,版权归码友网所有,如未经许可,禁止任何形式转载,违者必究。

发表评论

登录用户才能发表评论, 请 登 录 或者 注册