#go #grpc
#Вперед #grpc
Вопрос:
Я использую приведенный ниже код для подключения к серверу grpc, и clientConn
объект используется для всех последующих вызовов rpc. maxDelay
устанавливается равным 5 секундам. Теперь из-за какой-то проблемы на сервере он не отвечает на вызов grpc. Итак, мой клиент долго ждет каждого вызова rpc. Нужно ли устанавливать время ожидания по-другому?
b := grpc.BackoffConfig{
MaxDelay: maxDelay,
}
clientConn, err := grpc.Dial(serverAddress, grpc.WithBackoffConfig(b), grpc.WithInsecure())
if err != nil {
log.Println("Dial failed!")
return err
}
Комментарии:
1.
MaxDelay
может быть неверным, вам нужно установить время истечения крайнего срока, чтобы соединение выдавало ошибку / исключение по тайм-ауту, проверьте ссылки grpc.io/blog/deadlines2. Задержки отката не являются тайм-аутами. Они определяют время ожидания между повторными попытками. Используйте контексты для тайм-аутов и / или отмены.
Ответ №1:
Вы можете изменить свой код, чтобы добавить тайм-аут, используя grpc.WithTimeout(5 * time.Second)
вместо использования MaxDelay
, и grpc.WithBackoffConfig(b)
которые предназначены для повторных попыток и задержки повторных попыток.
clientConn, err := grpc.Dial(serverAddress, grpc.WithTimeout(5 * time.Second), grpc.WithInsecure())
if err != nil {
log.Println("Dial failed!")
return err
}
Однако приведенное выше устарело, в качестве альтернативы вы можете использовать DialContext
и context.WithTimeout
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
clientConn, err := grpc.DialContext(ctx, serverAddress, grpc.WithInsecure())
if err != nil {
log.Println("Dial failed!")
return err
}
Ответ №2:
Используется context.WithTimeout
grpc.DialContext
для управления временем ожидания всех вызовов RPC текущего DialContext. Неудобно обрабатывать разные тайм-ауты одного / некоторых конкретных вызовов RPC.
Мы могли бы определить один пользовательский тайм-аут callOption
для обработки значения принудительного тайм-аута некоторых вызовов RPC в clientInterceptor
Определите пользовательский тайм-аут callOption
через EmptyCallOption
type TimeoutCallOption struct {
grpc.EmptyCallOption
forcedTimeout time.Duration
}
func WithForcedTimeout(forceTimeout time.Duration) TimeoutCallOption {
return TimeoutCallOption{forcedTimeout: forceTimeout}
}
Обрабатывать forcedTimeout
в UnaryClientInterceptor
func getForcedTimeout(callOptions []grpc.CallOption) (time.Duration, bool) {
for _, opt := range callOptions {
if co, ok := opt.(TimeoutCallOption); ok {
return co.forcedTimeout, true
}
}
return 0, false
}
func TimeoutInterceptor(t time.Duration) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
timeout := t
if v, ok := getForcedTimeout(opts); ok {
timeout = v
}
if timeout <= 0 {
return invoker(ctx, method, req, reply, cc, opts...)
}
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
return invoker(ctx, method, req, reply, cc, opts...)
}
}
Примеры использования
// The default timeout of RPC call of this conn is 3 seconds
conn, err := grpc.Dial(
address,
grpc.WithUnaryInterceptor(TimeoutInterceptor(time.Duration(3)*time.Second)), ...)
....
c := pb.NewGreeterClient(conn)
c.SayHello(context.Background(), amp;pb.HelloRequest{Name: "world"},
WithForcedTimeout(time.Duration(10)*time.Second))
// The timeout of SayHello RPC call is 10 seconds