¿Qué hace golang-grpc?
Proporciona pautas de uso de gRPC, organización de protobuf y patrones listos para producción para microservicios en Golang. Utiliza al implementar, revisar o depurar servidores/clientes gRPC, escribir archivos proto, configurar interceptores, manejar errores gRPC con códigos de estado, configurar TLS/mTLS, probar con bufconn o trabajar con RPCs de streaming.
Paquetes clave
google.golang.org/grpc— gRPC oficial para Gogoogle.golang.org/protobuf— runtime de protobufgithub.com/bufbuild/buf— compilación y gestión de protosgithub.com/grpc-ecosystem/go-grpc-middleware— interceptores
Estructura de protos por paquete
Organizar protos en carpetas separadas por dominio, no por idioma:
proto/
user/
v1/
user.proto
payment/
v1/
payment.proto
Importar protos con rutas relativas a la raíz del módulo:
import "user/v1/user.proto";
Códigos de estado gRPC comunes
| Código | Uso |
|---|---|
OK | Éxito |
InvalidArgument | Error de validación de entrada |
NotFound | Recurso no encontrado |
AlreadyExists | Conflicto al crear un recurso duplicado |
PermissionDenied | Autenticación/permiso fallido |
Unavailable | Servicio temporalmente no disponible |
Internal | Error interno del servidor |
Interceptores (middleware)
Usar interceptores unarios y de streaming desde go-grpc-middleware:
Cliente:
import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
)
conn, err := grpc.Dial(
target,
grpc.WithUnaryInterceptor(
grpc_middleware.ChainUnaryClient(
logging.UnaryClientInterceptor(logging.DefaultClientLogger()),
retry.UnaryClientInterceptor(retry.WithMax(3)),
),
),
)
Servidor:
import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
)
s := grpc.NewServer(
grpc.UnaryInterceptor(
grpc_middleware.ChainUnaryServer(
logging.UnaryServerInterceptor(logging.DefaultServerLogger()),
recovery.UnaryServerInterceptor(),
),
),
)
Manejo de errores
Retornar errores gRPC con códigos de estado apropiados:
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
return nil, status.Error(codes.InvalidArgument, "el nombre de usuario es obligatorio")
Configuración TLS/mTLS
Servidor con TLS:
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
s := grpc.NewServer(grpc.Creds(creds))
Cliente con TLS:
creds, err := credentials.NewClientTLSFromFile("ca.crt", "")
conn, err := grpc.Dial(target, grpc.WithTransportCredentials(creds))
mTLS en el servidor:
caCert, _ := os.ReadFile("ca.crt")
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caCert)
creds := credentials.NewTLS(&tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caPool,
})
s := grpc.NewServer(grpc.Creds(creds))
Pruebas con bufconn
Usar bufconn para pruebas sin conexión real:
import "google.golang.org/grpc/test/bufconn"
func TestService(t *testing.T) {
lis := bufconn.Listen(1024 * 1024)
s := grpc.NewServer()
pb.RegisterUserServiceServer(s, &server{})
go s.Serve(lis)
conn, err := grpc.Dial(
"bufnet",
grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
return lis.Dial()
}),
grpc.WithInsecure(),
)
// usar conn para llamar al servicio
}
Streaming RPC
Servidor de streaming:
func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
for _, u := range users {
if err := stream.Send(&pb.User{Id: u.Id, Name: u.Name}); err != nil {
return err
}
}
return nil
}
Cliente de streaming:
stream, err := client.ListUsers(ctx, &pb.ListUsersRequest{})
for {
user, err := stream.Recv()
if err == io.EOF {
break
}
// procesar usuario
}
Archivos de configuración recomendados
buf.gen.yamlpara generación de código con bufbuf.work.yamlpara workspaces de bufbuf.yamlpara configuración de lint y breaking changes