在 gRPC c# 中抛出 RpcException 后可以继续或回调吗?

时间:2021-03-17 22:07:39

标签: c# exception async-await grpc grpc-c#

我正在拦截器中处理 gRPC 服务器端处理程序异常:

public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
    TRequest request,
    ServerCallContext context,
    UnaryServerMethod<TRequest, TResponse> continuation)
{
    try
    {
        return await continuation(request, context);
    }
    catch (Exception ex)
    {
        LogAndRethrowException(ex);
        return null; // keep compiler happy
    }
}       

LogAndRethrowException 的相关部分如下所示:

s_log.Debug("Unhandled Exception processing gRPC request", ex); 
throw new RpcException(new Status(
    StatusCode.Unknown, "Intercepted Unhandled: " + ex.Message));

这工作正常。我在客户端收到异常,并且可以执行我需要在那里执行的必要处理,而且我正在将其记录在服务器上。

我现在需要修改它以使服务器应用程序崩溃,因为这是我工作的政策。但是我仍然需要在客户端获取异常。

在 gRPC 处理 RpcException 并将响应通过线路发送到客户端之后,现有的 gRPC 库中是否有任何方法可以立即挂钩(或获取某种回调)该点?

我确实可以访问服务器端使用的同步上下文,并且可以向其发送 Post 以使其抛出异常。但是,这可能不可靠,因为此时在拦截器中我实际上不在此同步上下文中,因此服务器可能会过早崩溃。 (另一方面,理想情况下,我希望它在将异常响应返回给客户端后立即崩溃,并且同步上下文可能有其他内容排队......)

我一直在寻找 ServerCallContext 中的钩子,或者尝试创造性地使用延续的方法,但我找不到任何好的解决方案。

我确实查看了实际处理异常并发送状态的代码,看看那里是否有任何提示。

在 ServerCallHandler.cs 中,HandleCall 方法如下所示:

public async Task HandleCall(ServerRpcNew newRpc, CompletionQueueSafeHandle cq)
{
    var asyncCall = new AsyncCallServer<TRequest, TResponse>(
        method.ResponseMarshaller.ContextualSerializer,
        method.RequestMarshaller.ContextualDeserializer,
        newRpc.Server);

    asyncCall.Initialize(newRpc.Call, cq);
    var finishedTask = asyncCall.ServerSideCallAsync();
    var requestStream = new ServerRequestStream<TRequest, TResponse>(asyncCall);
    var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall);

    Status status;
    AsyncCallServer<TRequest,TResponse>.ResponseWithFlags? responseWithFlags = null;
    var context = HandlerUtils.NewContext(newRpc, responseStream, asyncCall.CancellationToken);
    try
    {
        GrpcPreconditions.CheckArgument(await requestStream.MoveNext().ConfigureAwait(false));
        var request = requestStream.Current;
        var response = await handler(request, context).ConfigureAwait(false);
        status = context.Status;
        responseWithFlags = new AsyncCallServer<TRequest, TResponse>.ResponseWithFlags(response, HandlerUtils.GetWriteFlags(context.WriteOptions));
    }
    catch (Exception e)
    {
        if (!(e is RpcException))
        {
            Logger.Warning(e, "Exception occurred in the handler or an interceptor.");
        }
        status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
    }
    try
    {
        await asyncCall.SendStatusFromServerAsync(status, context.ResponseTrailers, responseWithFlags).ConfigureAwait(false);
    }
    catch (Exception)
    {
        asyncCall.Cancel();
        throw;
    }
    await finishedTask.ConfigureAwait(false);
}

finishedTask(在开始时设置为 asyncCall.ServerSideCallAsync())看起来很有希望。我没有正确地研究它,但正如它在最后等待的那样,这能以某种方式提供一种方法吗?

(而且,有点远……但看起来,使用 java gRPC,您可以通过调用 responseObserver.onError 将异常“发送”回客户端 - C# 中是否有类似的东西我可以以某种方式使用 gRPC 作为仅抛出 RpcException 以获得适当响应的替代方法?)

0 个答案:

没有答案
相关问题