Сводка: если клиентский канал находится в состоянии 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(&context, request, &response);
/ server.cc
grpc::ServerBuilder builder;
builder.AddListeningPort(endpoint, ::grpc::InsecureServerCredentials());
builder.RegisterService(&rpc_service);
std::unique_ptrgrpc::Server grpc_server_ = builder.BuildAndStart();
Что вы ожидали увидеть?
Клиент должен сделать успешный вызов после сброса сети.
Что вы видели вместо этого?
Клиент не получает ответс сервера.
Что еще мы должны знать о вашем проекте / среде?
Когда сетевое соединение восстанавливается и клиент не получает ответ отсервер 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 дней, поэтому я публикую здесь какЧто ж.Ссылка на выпуск GTP GPRC: https://github.com/grpc/grpc/issues/18554