Укажите крайний срок с помощью Go gRPC для одноранговых соединений.

Согласно документации gRPC, клиенты могут указать крайние сроки, чтобы определить, как долго клиент будет ждать на сервере перед завершением с ошибкой DEADLINE_EXCEEDED. В документации упоминается, что разные языки имеют разные реализации и что некоторые языки не имеют значений по умолчанию.

Действительно, быстрое сочетание клавиш CTRL + F для "крайнего срока" в документации Go gRPC не дает результатов. . Что я обнаружил, так это WithTimeout на дозвоне для TCP-соединения.

Реализовано следующим образом (из примера helloworld):

package main

import (
    "log"
    "os"
    "time"

    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

const (
    address     = "localhost:50051"
    defaultName = "world"
    deadline    = 20 
)

func main() {
    // Set up a connection to the server with a timeout 
    conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithTimeout(time.Duration(deadline)*time.Second)
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Contact the server and print out its response.
    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.Message)
}

Код вызовет ошибку, только если клиент не может подключиться через 20 секунд. Результат будет примерно таким:

2016/05/24 09:02:54 grpc: Conn.resetTransport failed to create client transport: connection error: desc = "transport: dial tcp [::1]:3265: getsockopt: connection refused"; Reconnecting to "localhost:3265"
2016/05/24 09:02:54 Failed to dial localhost:3265: grpc: timed out trying to connect; please retry.
2016/05/24 09:02:54 could not greet: rpc error: code = 2 desc = grpc: the client connection is closing

Как указано в заголовке вопроса, система, с которой я работаю, является одноранговой, поэтому нет центрального, всегда работающего сервера, и поэтому система повторных попыток, которую реализует gRPC, прекрасна. Однако я на самом деле ищу крайние сроки, потому что, если удаленный компьютер подключается, но серверу требуется> 20 секунд, чтобы ответить, в контексте WithTimeout не возникнет никаких исключений.

Для меня полной победой будет система тайм-аутов / дедлайнов, где:

  • если клиент не может подключиться, по истечении тайм-аута возвращается ошибка
  • если клиент подключается, но сервер не отвечает до истечения времени ожидания, возвращается ошибка.
  • если клиент подключается, но соединение прерывается до истечения времени ожидания, возвращается ошибка.

Однако я чувствую, что мне понадобится некоторая комбинация управления подключением плюс управление сроками gRPC. Кто-нибудь знает, как в Go реализовать дедлайны?


person bbengfort    schedule 24.05.2016    source источник
comment
Я не уверен, что понимаю вопросы. У вас уже есть тайм-аут в Dial, поэтому он не может зависать более 20 секунд (BTW, крайний срок - это абсолютное время, тайм-аут - это продолжительность). Если вы хотите, чтобы вызов rpc имел тайм-аут, поместите контекст с тайм-аутом или крайним сроком в этот вызов.   -  person JimB    schedule 24.05.2016
comment
Я согласен с @JimB. Вы можете легко настроить горутину с таймером, который закрывает соединение (и / или печатает сообщение и т. Д.), Если он не возвращается до истечения таймера.   -  person Anfernee    schedule 24.05.2016
comment
Тайм-аут в дозвоне - это тайм-аут соединения, если сервер подключается, то этот тайм-аут не соблюдается. Вторая часть относится к функции крайнего срока в gRPC - где, если сервер не возвращает ответное сообщение в течение определенного периода времени, возникает ошибка DEADLINE_EXCEEDED (все заглавные буквы из-за кроссплатформенной природы gRPC).   -  person bbengfort    schedule 25.05.2016


Ответы (2)


Согласно WithTimeout примеру context

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    // Pass a context with a timeout to tell a blocking function that it
    // should abandon its work after the timeout elapses.
    ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    defer cancel()

    select {
        case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err()) // prints "context deadline exceeded"
    }
} 

Вы можете изменить helloworld example код клиента на время ожидания 100 мс:

ctx, _ := context.WithTimeout(context.Background(), 100 * time.Millisecond)
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
    log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
person akawhy    schedule 29.06.2017
comment
Прошло много времени с тех пор, как я смотрел на это, но вы правы - тайм-аут на стороне клиента, по-видимому, единственный способ решить эту проблему, согласно моему чтению библиотеки контекста. Я бы обновил ваше предложение использовать метод отмены, чтобы сервер не пытался ответить, когда клиент отменяет результат. - person bbengfort; 29.06.2017

Вам следует более внимательно присмотреться к пакету context. GRPC был построен с использованием контекстов как фундаментальной части. Возможно, вам понадобится grpc.Dial контекст и client.SayHello контекст, которые будут построены на основе связанной информации, но это должно быть довольно просто.

person matt.s    schedule 24.05.2016