Мне нужна идея получше, как использовать GetAwaiter().OnCompleted для Grpc AsyncUnaryCall

#c# #async-await #grpc

#c# #асинхронное ожидание #grpc

Вопрос:

Я пытаюсь прочитать заголовок / метаданные ответа из вызова службы grpc. Чтобы сделать это в одной центральной точке, я использую пользовательский вызывающий вызов, который инкапсулирует реальную основу HttpClientInvoker .

Мой вызывающий код пока очень прост, просто позвоните 3 раза

 var greeter = new GreeterClient(new MyCustomCallInvoker());
await greeter.SayHelloAsync(new HelloRequest { Name = nameof(TestSayHello) });
await greeter.SayHelloAsync(new HelloRequest { Name = nameof(TestSayHello) });
await greeter.SayHelloAsync(new HelloRequest { Name = nameof(TestSayHello) });
  

Пользовательский вызыватель делает что-то вроде этого

 public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
                                    Method<TRequest, TResponse> method,
                                    string host,
                                    CallOptions options,
                                    TRequest request)
{
   var result = Invoker.AsyncUnaryCall<TRequest, TResponse>(method, host, options, request);
   Action updateMetadata = delegate
   {
      var trailers = result.GetTrailers();
      UpdateMetadata(trailers);
   };
   result.GetAwaiter().OnCompleted(updateMetadata);
   return resu<
}
  

Я знаю, что использование GetAwaiter() не очень приятно. К сожалению, вызывающий здесь не используется Task и AsyncUnaryCall закрыт.

Однако вызов приветствия прошел 2 раза, прежде чем трейлеры были обновлены в первый раз.

Я не видел способа обновить локальные трейлеры сразу после Invoker.AsyncUnaryCall прохождения и до того, как он вернется к вызывающему коду, не блокируя процесс. (РЕДАКТИРОВАТЬ: если я просто поставлю консоль.Напишите («готово») в OnCompleted, он работает так, как ожидалось.)

Это возможно каким-то образом?

Комментарии:

1. @yogihosting это не связано с моим вопросом. я думаю, это просто seo-ссылка для вашего сайта.

2. Я полагаю, вы имеете в виду CallInvoker/DefaultCallInvoker нет HttpClientInvoker ? Кроме того, откуда OnCompleted берется?

3. @IanKemp Я предполагаю, что это так: learn.microsoft.com/en-us/dotnet/api /…

4. @ChrisYungmann Ах, я ожидал чего-то в gRPC … не отправленный метод, помеченный как «Этот API поддерживает инфраструктуру продукта и не предназначен для использования непосредственно из вашего кода». IDisposable , вам, вероятно, не следует использовать этот метод, учитывая документацию.

5. Извините, вы правы @IDisposable ответ, который я опубликовал, не будет работать из-за упомянутой вами проблемы. Может быть, это поможет? github.com/grpc/grpc/issues/21489 Похоже, вам следует вернуть новый AsyncUnaryCall экземпляр, который затем вы можете предоставить любой асинхронной логике.

Ответ №1:

Потребовалось некоторое время, но я нашел решение, которым хочу поделиться с сообществом. Проблема здесь заключалась в том, что AsyncUnaryCall(который проводит TaskAwaiter) закрыт.

Я заглядываю в исходный код grpc и вижу, что AsyncUnaryCall — это просто контейнер для нескольких обратных вызовов. прототипы ctor выглядят следующим образом:

 public AsyncUnaryCall(Task<TResponse> responseAsync, Task<Metadata> responseHeadersAsync, Func<Status> getStatusFunc, Func<Metadata> getTrailersFunc, Action disposeAction);

public AsyncUnaryCall(Task<TResponse> responseAsync, Func<object, Task<Metadata>> responseHeadersAsync, Func<object, Status> getStatusFunc, Func<object, Metadata> getTrailersFunc, Action<object> disposeAction, object state)
  

Это было решением моей проблемы: создать второй AsyncUnaryCall в качестве прокси, который инкапсулирует исходный AsyncUnaryCall и слегка модифицирует их, добавив GetTrailers в обратный вызов ResponseAsync.

Надеюсь, это поможет кому-то в будущем.