#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 месяца