Как проверить сроки в промежуточном программном обеспечении сервера

Согласно документу, сервер gRPC может проверять сроки как

if ctx.Err() == context.Canceled {
    return status.New(codes.Canceled, "Client cancelled, abandoning.")
}

И я пытаюсь справиться с этим в примере привет

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Received: %v", in.GetName())

    select {
    case <- time.After(time.Second):
        // simulate some operation here
    case <- ctx.Done():
        if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded{
            return nil, status.New(codes.Canceled, "Client cancelled, abandoning.").Err()
        }
    }

    return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

С кодами тестовых клиентов

    ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*600)
    defer cancel()
    r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

Мой вопрос: как проверить крайний срок в промежуточном программном обеспечении (или перехватчике) на сервере gRPC?

Что я пробовал

type serverStats struct {}

func (h *serverStats) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
    fmt.Println("tag rpc")
    return ctx
}

func (h *serverStats) HandleRPC(ctx context.Context, s stats.RPCStats) {
    fmt.Println("handle rpc")
    if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded {
        fmt.Printf("HandleRPC: Client err %+v \n", ctx.Err())
    }
}

...

    s := grpc.NewServer(grpc.StatsHandler(&serverStats{}))

Однако HandleRPC запускается несколько раз и НЕ может возвращать коды состояния ошибки.


person zangw    schedule 04.02.2021    source источник


Ответы (2)


Я использовал пример приветствия в качестве основы для написания примера перехватчика времени ожидания.

Возможно, есть лучшие способы сделать это, но вот оно:

/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Package main implements a server for Greeter service.
package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"
    "google.golang.org/grpc/status"
)

const (
    port = ":50051"
)

func timeoutInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    var err error
    var result interface{}

    done := make(chan struct{})

    go func() {
        result, err = handler(ctx, req)
        done <- struct{}{}
    }()

    select {
    case <-ctx.Done():
        if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded {
            return nil, status.New(codes.Canceled, "Client cancelled, abandoning.").Err()
        }
    case <-done:
    }
    return result, err
}

// server is used to implement helloworld.GreeterServer.
type server struct {
    pb.UnimplementedGreeterServer
}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Received: %v", in.GetName())
    return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer(grpc.ChainUnaryInterceptor(timeoutInterceptor))
    pb.RegisterGreeterServer(s, &server{})
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}
person Renato Aquino    schedule 04.02.2021

Вы можете использовать эту библиотеку.

Перехватчик имеет информацию о контексте запроса.

И я не думаю, что вам нужно самостоятельно разбираться с крайним сроком на стороне сервера (особенно в унарном запросе), ошибка 4 DEADLINE_EXCEEDED обрабатывается библиотекой grpc. Вам нужно только убедиться, что клиент установил крайний срок, и крайний срок находится в приемлемом диапазоне.

func UnaryInterceptor() grpc.UnaryServerInterceptor {
    return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
        //check whether client set the deadline
        d,ok := ctx.Deadline()
        if !ok {
            //return some error
        }
        timeout := d.Sub(time.Now())
        //check timeout range
        if timeout < 5*time.Second || timeout > 30*time.Second {
            //return some error
        }
}
person Franci    schedule 30.05.2021