Да, хорошо иметь одно клиентское соединение GRPC для каждой услуги. Более того, я не вижу здесь других вариантов. GRPC выполняет всю тяжелую работу под капотом: например, вам не нужно писать свой собственный пул клиентских подключений (как вы это сделали бы для типичной СУБД), потому что он не обеспечит лучших результатов, чем одно GRPC-соединение.
Однако я бы посоветовал вам избегать использования глобальных переменных и функций инициализации, особенно для настройки сети. Также вам не нужно создавать клиента GRPC (c := user.NewUserClient(userConn)
) каждый раз, когда вы отправляете запрос в службу GRPC: это просто дополнительная работа для сборщика мусора, вы можете создать единственный экземпляр клиента во время запуска приложения .
Обновление
Предполагая, что вы пишете серверное приложение (поскольку это видно из метода, который вы вызываете в удаленной службе GRPC), вы можете просто определить тип, который будет содержать все объекты, которые имеют тот же срок жизни, что и все приложение. сам. Согласно традиции, эти типы обычно называют «контекстом сервера», хотя это немного сбивает с толку, потому что Go имеет очень важную концепцию context
в своей стандартной библиотеке.
// this type contains state of the server
type serverContext struct {
// client to GRPC service
userClient user.UserClient
// default timeout
timeout time.Duration
// some other useful objects, like config
// or logger (to replace global logging)
// (...)
}
// constructor for server context
func newServerContext(endpoint string) (*serverContext, error) {
userConn, err := grpc.Dial(endpoint, grpc.WithInsecure())
if err != nil {
return nil, err
}
ctx := &serverContext{
userClient: user.NewUserClient(userConn),
timeout: time.Second,
}
return ctx, nil
}
type server struct {
context *serverContext
}
func (s *server) Handler(ctx context.Context, request *Request) (*Response, error) {
clientCtx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
response, err := c.GetUserFromTokenID(
clientCtx,
&user.GetUserFromTokenRequest{
TransactionID: transactionID,
OathToken: *oathToken,
},
)
if err != nil {
return nil, err
}
// ...
}
func main() {
serverCtx, err := newServerContext(os.Getenv("USER_SERVICE_URL"))
if err != nil {
log.Fatal(err)
}
s := &server{serverCtx}
// listen and serve etc...
}
Детали могут меняться в зависимости от того, над чем вы на самом деле работаете, но я просто хотел показать, что гораздо лучше инкапсулировать состояние вашего приложения в экземпляр другого типа, чем заражать глобальное пространство имен.