Я пытаюсь передать контекст из входящей конечной точки gRPC в программу, которая отвечает за отправку другого запроса во внешнюю службу, но я получаю Error occurred: context canceled
от вызова функции ctxhttp.Get
в программе:
package main
import (
"fmt"
"net"
"net/http"
"os"
"sync"
"golang.org/x/net/context/ctxhttp"
dummy_service "github.com/myorg/testing-apps/dummy-proto/gogenproto/dummy/service"
"github.com/myorg/testing-apps/dummy-proto/gogenproto/dummy/service/status"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
func main() {
var err error
grpcServer := grpc.NewServer()
server := NewServer()
dummy_service.RegisterDummyServer(grpcServer, server)
reflection.Register(grpcServer)
lis, err := net.Listen("tcp", ":9020")
if err != nil {
fmt.Printf("Failed to listen: %+v", err)
os.Exit(-1)
}
defer lis.Close()
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Starting gRPC Server")
if err := grpcServer.Serve(lis); err != nil {
fmt.Printf("Failed to serve gRPC: %+v", err)
os.Exit(-1)
}
}()
wg.Wait()
}
type server struct{}
func NewServer() server {
return server{}
}
func (s server) Status(ctx context.Context, in *status.StatusRequest) (*status.StatusResponse, error) {
go func(ctx context.Context) {
client := http.Client{}
// it's important to send the ctx from the parent function here because it contains
// a correlation-id which was inserted using grpc middleware, and the external service
// prints this value in the logs to tie everything together
if _, err := ctxhttp.Get(ctx, &client, "http://localhost:4567"); err != nil {
fmt.Println("Error encountered:", err)
return
}
fmt.Println("No error encountered")
}(ctx)
response := status.StatusResponse{
Status: status.StatusResponse_SUCCESS,
}
// if I enable the following, everything works, and I get "No error encountered"
// time.Sleep(10 * time.Millisecond)
return &response, nil
}
Если я добавлю time.Sleep()
внутри вызывающей функции, процедура завершится успешно, как и ожидалось, и не получит никакой ошибки.Кажется, что контекст родительской функции становится отмененным, как только он возвращается, и, поскольку родительский объект заканчивается перед процедурой, контекст, переданный в программу, получает ошибку context canceled
.
Я понимаю, что яможно решить эту проблему, вызвав функцию вызова, ожидающую завершения работы программы, что предотвратит отмену контекста, но я не хочу этого делать, поскольку хочу, чтобы функция немедленно возвращалась, чтобы клиент, попавший в конечную точку, получилответ как можно скорее, пока программа продолжает обрабатывать в фоновом режиме.
Я также могу решить эту проблему, не используя переданный в ctx
и вместо этого context.Background()
в моей программе, однако я хочуиспользуйте входящий ctx
, потому что он содержит значение correlation-id
, которое было вставлено промежуточным программным обеспечением grpc и должно быть передано как часть исходящего запроса, который выполняет программа, чтобы следующий сервер мог напечатать этот correlation-id
в своемпротоколировать сообщения, чтобы связать запросы вместе.
В итоге я решил эту проблему, извлекая correlation-id
из входящего контекста и вставляя его в новый context.Background()
в программе, но я хотел избежать этого, так как он добавляет кучу шаблонного кода вокруг каждого исходящегозапрос, который делает goroutine, вместо того, чтобы просто передавать контекст.
Может ли кто-нибудь объяснить мне, почему контекст отменяется, и сообщить мне, если есть решение «передового опыта» для этого типаситуации?Разве нельзя использовать контекст, переданный из вызывающей функции, в goroutine с gRPC?