Клиентский канал непригоден для использования после сброса сети

#c #grpc

#c #grpc

Вопрос:

Резюме: Если клиентский канал находится в READY состоянии и сеть отключена, канал становится непригодным для использования, и клиент не будет пытаться повторно подключиться к серверу после восстановления сетевого соединения. Канал не переходит из READY состояния в TRANSIENT_FAILURE DEADLINE_EXCEEDED состояние с ошибкой (крайний срок, установленный моим клиентским приложением).

Какую версию gRPC и какой язык вы используете?

1.17.2 Та же проблема возникает в версии 1.11.x C

Какая операционная система (Linux, Windows, …) и версия?

Клиент, работающий на Ubuntu 16.04. Сервер под управлением Windows Enterprise.

Что вы сделали?

Сервер и клиент оба запущены в подключенной сети. Я могу успешно совершать вызовы и получать ответы от сервера. Когда сеть выключена, сервер получает "Disconnected client - Endpoint read failed" сообщение об ошибке. Некоторые другие соответствующие поля в этом отладочном сообщении — "grpc_status":14 (UNAVAILABLE), "occured_during_write":0, "description":"An established connection was aborted by the software in your host machine" .

Во время отключения от сети клиент вообще не распечатывает никаких журналов (используя GRPC_TRACE=connectivity_state,call_error,op_failure,server_channel,client_channel,channel GRPC_VERBOSITY=DEBUG ).

После повторного включения сети ни на сервере, ни на клиенте нет журналов регистрации. Попытка выполнить вызов с использованием клиента (отправить запрос на запуск) приводит к повторной DEADLINE_EXCEEDED ошибке. Отключение сетевого подключения в это время не приводит к "Disconnected client" ошибке на стороне сервера.

Контекст клиента настроен на использование крайнего срока (тестировалось с 2 и 10 секундами). В этом случае используются синхронные вызовы.

Фрагменты кода:

/rpc_service.proto

 syntax = "proto3";

import "google/rpc/status.proto";

message RpcRequest {
}

message RpcResponse {
}

service RpcService{
rpc Call(RpcRequest) returns (RpcResponse);
}
  

/client.cc

Инициализация:

 std::unique_ptrRpcService::Stub stub_ = RpcService::NewStub(::grpc::CreateChannel(
server_endpoint, ::grpc::InsecureChannelCredentials()));
  

Отправка rpc-запроса:

 ::grpc::ClientContext context;
context.set_deadline(
gpr_time_from_micros(call_timeout_.InMicroseconds(), GPR_TIMESPAN));
RpcRequest request;
RpcResponse response;
::grpc::Status grpc_status = stub_->Call(amp;context, request, amp;response);
  

/server.cc

 grpc::ServerBuilder builder;
builder.AddListeningPort(endpoint, ::grpc::InsecureServerCredentials());
builder.RegisterService(amp;rpc_service);
std::unique_ptrgrpc::Server grpc_server_ = builder.BuildAndStart();
  

What did you expect to see?

Client should make a successful call after a network reset.

What did you see instead?

Client fails to receive a response from the server.

Anything else we should know about your project / environment?

Когда сетевое соединение восстанавливается и клиенту не удается получить ответ от сервера, tcpdump фиксирует отправку клиентом некоторых пакетов. Запуск как клиента, так и сервера с включенной сетью, а затем отключение сети не приводит к появлению каких-либо сообщений об ошибках, пока не будет предпринята попытка вызова. Это тот же результат, что и при запуске клиента и сервера с отключенной сетью. После попытки вызова клиент перейдет из IDLE в CONNECTING , а затем начнет переходить из одного состояния в другое между CONNECTING и TRANSIENT_FAILURE состояниями (пытаясь восстановить соединение с экспоненциальным отключением), пока соединение не будет восстановлено.

Если клиент запущен с подключенной сетью, но не отправляет запрос, и сеть отключена, сервер не получает сообщение об ошибке отключенного клиента. Пока не будет выполнен вызов, клиент остается в "IDLE" .

Если клиент инициализирован и вызов выполнен в отключенной сети, то клиент перейдет в CONNECTING состояние (с экспоненциальным отключением максимум до 2 минут, когда клиент будет находиться в TRANSIENT_FAILURE состоянии). Как только сеть подключена, соединение будет восстановлено в следующий раз, когда канал перейдет в CONNECTING состояние, а клиент перейдет в READY состояние. После этого каждый вызов будет успешным, пока сеть не будет сброшена. Отключение сети после того, как клиент находится в READY состоянии, не приведет к переводу клиента из READY состояния.

Вкратце: пока не будет выполнен вызов, клиент будет оставаться в "IDLE" состоянии независимо от состояния сети. Как только вызов выполнен, клиент попытается установить соединение, введя CONNECTING состояние. Если соединение не найдено, произойдет сбой перехода между CONNECTING и TRANSIENT_FAILURE состояниями. Как только соединение найдено, клиент перейдет в READY состояние. Отсюда, если соединение потеряно, клиент не будет пытаться снова войти в CONNECTING состояние.

Аналогичная проблема (закрыта), с которой я столкнулся:

https://github.com/grpc/grpc/issues/16974

Известное исправление:

Создавайте новый канал при каждом вызове.

Неудачные попытки исправления:

 Set GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA = 0
  

Вопросы:

Должен ли клиент иметь возможность использовать уже созданный канал после сброса сети?

Нужно ли перезапускать канал при сбросе сети?

Примечание: Я открыл заявку на grpc github и не получал ответа в течение 5 дней, поэтому я также публикую здесь. Проблема со ссылкой на grpc github:https://github.com/grpc/grpc/issues/18554

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

1. да, и вы получите нулевой ответ на такой подробный вопрос через год и 4 месяца