grpc

package module
v1.13.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 6, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

README

Forge gRPC, a gRPC server with health checking, server reflection and TLS that runs inside a Go application built on Forge

Forge gRPC: a gRPC server for Go apps built on Forge

Forge gRPC is the gRPC extension for Forge, the Go backend framework. It runs a grpc-go server next to your HTTP server and ties it to the app's lifecycle, with the standard gRPC health checking protocol, server reflection for tools like grpcurl, TLS and mutual TLS, keepalive and message size limits, request logging, OpenTelemetry spans and metrics, and graceful shutdown. You hand it your generated services with WithService or WithServiceRegistrar, and it registers them before the server starts.

Go CI Go Reference Apache 2.0

Documentation · Quick start · Registering services · Features · Health checks · TLS · Tracing and metrics · Clients · Configuration · FAQ

Quick start

You need Go 1.26 or later. Add Forge and the extension to your module:

go get github.com/xraph/forge@latest
go get github.com/xraph/forge/extensions/grpc@latest

Save this as main.go. It starts a gRPC server on port 50051 with health checks and reflection, and adds a health checker for one dependency:

package main

import (
	"context"
	"log"

	"github.com/xraph/forge"
	grpcext "github.com/xraph/forge/extensions/grpc"
)

type dbCheck struct{}

func (dbCheck) Check(ctx context.Context) error {
	return nil // ping your database here
}

func main() {
	app := forge.NewApp(forge.AppConfig{
		Name:        "billing",
		HTTPAddress: ":8080",
		Extensions: []forge.Extension{
			grpcext.NewExtension(
				grpcext.WithAddress(":50051"),
				grpcext.WithReflection(true),
				grpcext.WithHealthCheck(true),
				grpcext.WithServiceRegistrar(func(app forge.App, s grpcext.GRPC) error {
					s.RegisterHealthChecker("billing.db", dbCheck{})

					return nil
				}),
			),
		},
	})

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

The package is named grpc, so the example imports it as grpcext to keep it apart from google.golang.org/grpc. Run it with go mod tidy && go run ., then talk to it with grpcurl:

grpcurl -plaintext localhost:50051 list
# grpc.health.v1.Health
# grpc.reflection.v1.ServerReflection
# grpc.reflection.v1alpha.ServerReflection

grpcurl -plaintext -d '{"service":"billing.db"}' localhost:50051 grpc.health.v1.Health/Check
# { "status": "SERVING" }

When you stop the app, the server stops taking new calls and waits for the ones in flight. If the app's shutdown timeout runs out first, it stops hard and cancels what's left.

Registering your own services

Pass your services to the extension when you create it. WithService takes the generated descriptor and your implementation:

grpcext.NewExtension(
	grpcext.WithAddress(":50051"),
	grpcext.WithService(&pb.Greeter_ServiceDesc, &greeter{}),
)

If the implementation needs something from the container, or you want an interceptor or a health checker next to it, use WithServiceRegistrar. Your function gets the app and the server, and the extension calls it once, while it builds the server inside app.Start:

grpcext.WithServiceRegistrar(func(app forge.App, s grpcext.GRPC) error {
	db, err := forge.Inject[*sql.DB](app.Container())
	if err != nil {
		return err
	}

	s.AddUnaryInterceptor(authInterceptor)
	s.RegisterHealthChecker("greeter", greeterCheck{db: db})

	return s.RegisterService(&pb.Greeter_ServiceDesc, &greeter{db: db})
}),

A registrar that returns an error fails app.Start with that error wrapped. So does a service the server can't take, which comes back as ErrInvalidService: an implementation that doesn't satisfy the descriptor's server interface, or a service name that's already registered. The health and reflection services count when they're on. grpc-go would end the process for either one at Start, so the extension checks them when you register.

The registrar runs inside the constructor of the gRPC service, so it can resolve anything you provided on the container before app.Start and anything an earlier extension registered. Resolving grpcext.GRPC or *grpcext.GRPCService from inside it is a circular dependency. WithConfig replaces the whole config, registrars included, so put WithService and WithServiceRegistrar after it.

From another extension or a hook

The server doesn't listen until the very end of app.Start. The extension starts it from a PhaseAfterStart hook with priority math.MinInt, so it runs after every other hook, and until then you can register from anywhere that reaches the container. An extension that lists grpc in its dependencies resolves the server in its Register or its Start:

func (e *Extension) Register(app forge.App) error {
	if err := e.BaseExtension.Register(app); err != nil {
		return err
	}

	srv, err := forge.Inject[grpcext.GRPC](app.Container())
	if err != nil {
		return err
	}

	return srv.RegisterService(&pb.Ledger_ServiceDesc, e.ledger)
}

A PhaseAfterRegister hook works too, and so does a PhaseAfterStart hook at any higher priority. Once app.Start returns, the server is serving and RegisterService returns ErrRegisterAfterStart. The flip side is that nothing can dial the server from inside app.Start: an extension's Start or an after-start hook runs before the port is open. If the port is taken or the TLS files don't load, app.Start fails with the error from that last hook.

grpc-go can't add a service to a server that's already serving, which is why all of this has to happen first. Here's what each call does on either side of the server's start:

What Before it serves Once it serves
RegisterService Buffered and served Returns ErrRegisterAfterStart
AddUnaryInterceptor, AddStreamInterceptor Chained onto the server Stored, never called
RegisterHealthChecker Works Works, checkers are looked up on each call
GetServer nil The grpc.Server

Don't call a generated pb.RegisterXxxServer on GetServer(). It's nil until Start, and on a serving server grpc-go stops the process with grpc: Server.RegisterService after Server.Serve. Use RegisterService(&pb.Xxx_ServiceDesc, impl) instead.

If you'd like to own the server's start and stop yourself, grpcext.NewGRPCServer(cfg, app.Logger(), app.Metrics()) gives you one outside the extension, and you call Start and GracefulStop on it. Create it after forge.NewApp, because it reads the OpenTelemetry globals NewApp installs when it starts.

The grpc-advanced example registers a greeter, an auth interceptor and a health checker through WithServiceRegistrar.

Features

You need Forge gRPC gives you
A gRPC server that starts and stops with the app A grpc.Server built from config when the extension starts, stopped gracefully when the app stops
Kubernetes and load balancer probes The grpc.health.v1.Health service, with your own per-service checkers
grpcurl, Postman and other dynamic clients Server reflection, on by default
Encrypted traffic TLS from a certificate and key, and mutual TLS with a client CA
Limits Receive and send size limits (4MB each by default), max concurrent streams, connection timeout and keepalive enforcement
Visibility Debug logs per call, error logs on failure, an OpenTelemetry span per call, and the rpc.server.call.duration histogram
Calling other gRPC services ClientDialOptions(), dial options that trace outbound calls
A quick look at the server GetStats counters and GetServices with each service's methods

Health checks

With enable_health_check on (the default), the server answers the gRPC health checking protocol. A checker is anything with Check(ctx context.Context) error, registered under a service name with RegisterHealthChecker. You can add one at any time, since checkers are looked up on each call.

Request Answer
service empty SERVING
A service whose checker returns nil SERVING
A service whose checker returns an error NOT_SERVING
A service with no checker registered SERVING

Watch sends the current status, runs the checker again every five seconds and sends a new message whenever the answer changes. When the server stops it sends NOT_SERVING and closes the stream, so a watching probe never holds up shutdown. List returns the empty name and every service that has a checker, each with its current status.

The extension's own Health method only reports whether the extension has started. It doesn't call your checkers.

TLS and mTLS

grpcext.NewExtension(
	grpcext.WithTLS("server.crt", "server.key", ""),
)

// Mutual TLS: require and verify client certificates against ca.crt.
grpcext.NewExtension(
	grpcext.WithTLS("server.crt", "server.key", "ca.crt"),
	grpcext.WithClientAuth(true),
)

Config validation fails at startup if TLS is on without a certificate and key, or client auth is on without a CA file. If the files are set but can't be loaded (a wrong path, a bad PEM, a CA file with no certificates in it), the server doesn't start and app.Start returns failed to load TLS credentials with the cause. It never falls back to plaintext.

Logging, tracing and metrics

enable_logging, enable_metrics and enable_tracing are all on by default. Logging writes a debug line when a call starts and ends, and an error line with the method, duration and error when it fails.

Spans

With enable_tracing on, every served call gets a server span named after the method (billing.Invoices/Get), made by otelgrpc. It carries the OpenTelemetry semconv v1.43 attributes:

Attribute Example
rpc.system.name grpc
rpc.method billing.Invoices/Get
rpc.response.status_code OK, NOT_FOUND, DEADLINE_EXCEEDED

A traceparent in the incoming metadata makes the span a child of the caller's trace. Server-side error codes such as INTERNAL mark the span as an error. Client-error codes such as NOT_FOUND don't. Health and reflection calls get no span, the same way Forge's HTTP tracing skips /_/.

The call's context also carries trace_id and span_id, as it does for HTTP requests, so a logger built from it stamps both on every line. The server uses the OpenTelemetry globals that forge.New installs, and reads them when it starts. Your app's tracing config (endpoint, sampling) is what sends the spans anywhere, see observability.

If you already added your own otelgrpc server handler, remove it. You'd get two spans per call.

Leave OTEL_SEMCONV_STABILITY_OPT_IN unset. If it holds rpc/old or rpc/dup, otelgrpc puts the older attributes on the span (rpc.service, a short rpc.method and a numeric status code), while rpc_server_call_duration_seconds keeps the v1.43 labels. Spans and metrics stop lining up.

Metrics

Every served call lands in one histogram, rpc.server.call.duration, recorded through the app's Forge metrics registry. Prometheus exports it as rpc_server_call_duration_seconds:

Label Value
rpc_system_name grpc
rpc_method billing.Invoices/Get
rpc_response_status_code OK, NOT_FOUND, and so on

The buckets run from 5ms to 10s. Health and reflection calls aren't recorded. A stream records the whole call, from open to close. A handler that returns a bare context.Canceled or context.DeadlineExceeded is recorded as CANCELLED or DEADLINE_EXCEEDED, which is what the client sees. With enable_metrics off nothing is recorded.

Two cases differ from the span. A call to a service or method the server doesn't have gets a span with UNIMPLEMENTED but no observation, because grpc-go rejects it before any interceptor runs. And a unary call whose handler succeeds but whose response fails to send is recorded as OK, while the wire and the span say otherwise.

The labels are the same keys and values that sit on the span, so a slow bucket and its trace line up. Two queries to start with:

# Calls per second, by method
sum by (rpc_method) (rate(rpc_server_call_duration_seconds_count[5m]))

# Failures per second, by method
sum by (rpc_method) (rate(rpc_server_call_duration_seconds_count{rpc_response_status_code!="OK"}[5m]))

The older forge_grpc_* series are gone. Nothing is emitted under the old names, so dashboards and alerts that use them go blank until you move them:

Old series Now
forge_grpc_unary_started_total, _succeeded_total, _failed_total rpc_server_call_duration_seconds_count, split by rpc_response_status_code
forge_grpc_unary_duration_seconds rpc_server_call_duration_seconds
forge_grpc_stream_succeeded_total, _failed_total rpc_server_call_duration_seconds_count, split by rpc_response_status_code
forge_grpc_stream_duration_seconds rpc_server_call_duration_seconds
forge_grpc_stream_active No replacement. GetStats().ActiveStreams still has it

Two label changes bite if you relabeled. The status code is the canonical name (NOT_FOUND), not the Go spelling (NotFound) the old code label held, and rpc_method has no leading slash, where the old method label had one.

GetStats() is unchanged. It returns the start time, RPC counts and active streams, and GetServices() lists registered services and their methods, with flags for client and server streaming.

Tracing outbound calls

ClientDialOptions() returns dial options that put a client span on every outbound call and send the trace context to the next service:

conn, err := grpc.NewClient("billing:50051",
	append(forgegrpc.ClientDialOptions(), grpc.WithTransportCredentials(creds))...)

Import the extension as forgegrpc "github.com/xraph/forge/extensions/grpc" here, to keep it apart from grpc-go. If you use conduit, Services.GRPC adds this handler for you, so don't pass ClientDialOptions() into it. gRPC appends stats handlers rather than replacing them, and a second one gives every call a second client span. Both read the OpenTelemetry globals when you call them, so call them after forge.New.

Configuration

The extension reads extensions.grpc from your config, falling back to grpc. Options you pass in code override the file.

extensions:
  grpc:
    address: ":50051"
    max_recv_msg_size: 4194304
    max_send_msg_size: 4194304
    max_concurrent_streams: 0     # 0 means no limit
    connection_timeout: 120s
    keepalive:
      time: 2h
      timeout: 20s
      enforcement_policy: true
      min_time: 5m
      permit_without_stream: false
    enable_tls: false
    tls_cert_file: ""
    tls_key_file: ""
    tls_ca_file: ""
    client_auth: false
    enable_health_check: true
    enable_reflection: true
    enable_metrics: true
    enable_tracing: true
    enable_logging: true
Key Option Default What it does
address WithAddress :50051 Listen address
max_recv_msg_size, max_send_msg_size WithMaxMessageSize (sets both) 4MB Message size limits
max_concurrent_streams WithMaxConcurrentStreams 0 Streams per connection, 0 for no limit
connection_timeout none 120s Time allowed for a new connection's handshake
keepalive.time, keepalive.timeout none 2h, 20s Ping interval and ack timeout
keepalive.enforcement_policy, min_time, permit_without_stream none true, 5m, false What the server accepts from client pings
enable_tls, tls_cert_file, tls_key_file, tls_ca_file WithTLS off Server TLS
client_auth WithClientAuth false Require and verify client certificates
enable_health_check WithHealthCheck true Registers the health service
enable_reflection WithReflection true Registers server reflection
enable_metrics WithMetrics true The rpc.server.call.duration histogram and the GetStats counters
enable_tracing WithTracing true A server span per call, and trace IDs on the log context
enable_logging none true Logging interceptors

keepalive.time also sets the server's maximum idle time for a connection, and twice that value is the maximum connection age, with keepalive.timeout as the grace period. With the defaults, an idle connection closes after two hours and any connection is recycled after four.

WithConfig replaces the whole config and WithRequireConfig(true) makes a missing config section an error at startup. WithService and WithServiceRegistrar have no key: a registrar is code, and the file can't set or remove one. The container holds *grpcext.GRPCService (also under the name grpc) and the grpcext.GRPC interface, backed by the same server.

Documentation

Topic Where
What the extension does Features
Every config key Configuration
Exported functions and types Functions, pkg.go.dev
grpc-go gRPC Go documentation
Extensions in general Forge extension system

FAQ

How do I add a gRPC server to a Go app built on Forge?

Add grpcext.NewExtension() to your app's Extensions and it listens on :50051 with health checks and reflection on. Add your services with grpcext.WithService(&pb.Xxx_ServiceDesc, impl), as registering your own services shows.

Does Forge gRPC support the gRPC health checking protocol?

Yes. It registers grpc.health.v1.Health, so Kubernetes gRPC probes and grpc_health_probe work against it. Register a checker per service name to report NOT_SERVING when a dependency is down.

How do I enable gRPC server reflection for grpcurl?

It's on by default. Turn it off with WithReflection(false) or enable_reflection: false.

Can I use mutual TLS?

Yes. Pass the CA file as the third argument to WithTLS and add WithClientAuth(true), and the server requires a client certificate signed by that CA.

Contributing

The extension is its own Go module inside the Forge repository. Test it from its directory:

cd extensions/grpc
GOWORK=off go test -race ./...
GOWORK=off golangci-lint run ./...

AGENTS.md has the full build, lint and release rules for the repository, and CONTRIBUTING.md has the contribution guidelines.

License

Forge gRPC is part of Forge and is available under the Apache License 2.0.

Documentation

Index

Constants

View Source
const (
	// ServiceKey is the DI key for the gRPC service.
	ServiceKey = "grpc"
)

DI container keys for gRPC extension services.

Variables

View Source
var (
	ErrNotStarted         = errors.New("grpc: server not started")
	ErrAlreadyStarted     = errors.New("grpc: server already started")
	ErrRegisterAfterStart = errors.New("grpc: services must be registered before Start")
	ErrInvalidService     = errors.New("grpc: invalid service registration")
	ErrServiceNotFound    = errors.New("grpc: service not found")
	ErrInvalidConfig      = errors.New("grpc: invalid configuration")
	ErrStartFailed        = errors.New("grpc: failed to start server")
	ErrStopFailed         = errors.New("grpc: failed to stop server")
	ErrHealthCheckFailed  = errors.New("grpc: health check failed")
)

Common gRPC errors.

Functions

func ClientDialOptions added in v1.12.9

func ClientDialOptions() []grpc.DialOption

ClientDialOptions returns dial options that trace outbound calls and carry the caller's trace context. It reads the OpenTelemetry globals when called, so call it after forge.New and keep the result.

func NewExtension

func NewExtension(opts ...ConfigOption) forge.Extension

NewExtension creates a new gRPC extension.

func NewExtensionWithConfig

func NewExtensionWithConfig(config Config) forge.Extension

NewExtensionWithConfig creates a new gRPC extension with a complete config.

Types

type Config

type Config struct {
	// Server settings
	Address              string          `json:"address"                mapstructure:"address"                yaml:"address"`
	MaxRecvMsgSize       int             `json:"max_recv_msg_size"      mapstructure:"max_recv_msg_size"      yaml:"max_recv_msg_size"`
	MaxSendMsgSize       int             `json:"max_send_msg_size"      mapstructure:"max_send_msg_size"      yaml:"max_send_msg_size"`
	MaxConcurrentStreams uint32          `json:"max_concurrent_streams" mapstructure:"max_concurrent_streams" yaml:"max_concurrent_streams"`
	ConnectionTimeout    time.Duration   `json:"connection_timeout"     mapstructure:"connection_timeout"     yaml:"connection_timeout"`
	Keepalive            KeepaliveConfig `json:"keepalive"              mapstructure:"keepalive"              yaml:"keepalive"`

	// TLS/mTLS
	EnableTLS   bool   `json:"enable_tls"              mapstructure:"enable_tls"    yaml:"enable_tls"`
	TLSCertFile string `json:"tls_cert_file,omitempty" mapstructure:"tls_cert_file" yaml:"tls_cert_file,omitempty"`
	TLSKeyFile  string `json:"tls_key_file,omitempty"  mapstructure:"tls_key_file"  yaml:"tls_key_file,omitempty"`
	TLSCAFile   string `json:"tls_ca_file,omitempty"   mapstructure:"tls_ca_file"   yaml:"tls_ca_file,omitempty"`
	ClientAuth  bool   `json:"client_auth"             mapstructure:"client_auth"   yaml:"client_auth"` // Require client cert

	// Health checking
	EnableHealthCheck bool `json:"enable_health_check" mapstructure:"enable_health_check" yaml:"enable_health_check"`

	// Reflection
	EnableReflection bool `json:"enable_reflection" mapstructure:"enable_reflection" yaml:"enable_reflection"`

	// Observability
	EnableMetrics bool `json:"enable_metrics" mapstructure:"enable_metrics" yaml:"enable_metrics"`
	EnableTracing bool `json:"enable_tracing" mapstructure:"enable_tracing" yaml:"enable_tracing"`
	EnableLogging bool `json:"enable_logging" mapstructure:"enable_logging" yaml:"enable_logging"`

	// Services set up the server before it starts. Add them with WithService
	// and WithServiceRegistrar. Only the extension applies them:
	// NewGRPCService and NewGRPCServer ignore this field.
	Services []ServiceRegistrar `json:"-" mapstructure:"-" yaml:"-"`

	// Config loading flags
	RequireConfig bool `json:"-" mapstructure:"-" yaml:"-"`
}

Config contains configuration for the gRPC extension.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns default gRPC configuration.

func (*Config) Validate

func (c *Config) Validate() error

Validate validates the configuration.

type ConfigOption

type ConfigOption func(*Config)

ConfigOption is a functional option for Config.

func WithAddress

func WithAddress(addr string) ConfigOption

func WithClientAuth

func WithClientAuth(enable bool) ConfigOption

func WithConfig

func WithConfig(config Config) ConfigOption

WithConfig replaces the whole config, including any services earlier options added. Put WithService and WithServiceRegistrar after it.

func WithHealthCheck

func WithHealthCheck(enable bool) ConfigOption

func WithMaxConcurrentStreams

func WithMaxConcurrentStreams(maxStreams uint32) ConfigOption

func WithMaxMessageSize

func WithMaxMessageSize(size int) ConfigOption

func WithMetrics

func WithMetrics(enable bool) ConfigOption

func WithReflection

func WithReflection(enable bool) ConfigOption

func WithRequireConfig

func WithRequireConfig(require bool) ConfigOption

func WithService added in v1.12.9

func WithService(desc *grpc.ServiceDesc, impl any) ConfigOption

WithService registers a service on the extension's server before it starts. desc is the generated descriptor, such as pb.Greeter_ServiceDesc, and impl implements its server interface.

func WithServiceRegistrar added in v1.12.9

func WithServiceRegistrar(register ServiceRegistrar) ConfigOption

WithServiceRegistrar adds a function that sets up the extension's server before it starts. Use it when an implementation needs dependencies from the container, or to add interceptors and health checkers next to the service.

func WithTLS

func WithTLS(certFile, keyFile, caFile string) ConfigOption

func WithTracing

func WithTracing(enable bool) ConfigOption

type Extension

type Extension struct {
	*forge.BaseExtension
	// contains filtered or unexported fields
}

Extension implements forge.Extension for gRPC functionality. The extension is now a lightweight facade that loads config and registers services.

func (*Extension) Health

func (e *Extension) Health(ctx context.Context) error

Health checks the extension health.

func (*Extension) Register

func (e *Extension) Register(app forge.App) error

Register registers the gRPC extension with the app. This method now only loads configuration and registers service constructors.

func (*Extension) Start

func (e *Extension) Start(ctx context.Context) error

Start builds the gRPC service, which runs the registrars from the options, and marks the extension as started. The server listens later, at the end of app.Start, from the hook Register adds.

func (*Extension) Stop

func (e *Extension) Stop(ctx context.Context) error

Stop stops the gRPC service and marks the extension as stopped.

type GRPC

type GRPC interface {
	// Service registration
	RegisterService(desc *grpc.ServiceDesc, impl any) error

	// Server management
	Start(ctx context.Context, addr string) error
	Stop(ctx context.Context) error
	GracefulStop(ctx context.Context) error

	// Interceptors
	AddUnaryInterceptor(interceptor grpc.UnaryServerInterceptor)
	AddStreamInterceptor(interceptor grpc.StreamServerInterceptor)

	// Health checking
	RegisterHealthChecker(service string, checker HealthChecker)

	// Server info. GetServer is nil until Start, and registering a generated
	// service on it once it serves crashes the process: use RegisterService
	// before Start.
	GetServer() *grpc.Server
	IsRunning() bool
	GetStats() ServerStats
	GetServices() []ServiceInfo

	// Health
	Ping(ctx context.Context) error
}

GRPC represents a unified gRPC server interface.

func NewGRPCServer

func NewGRPCServer(config Config, logger forge.Logger, metrics forge.Metrics) GRPC

NewGRPCServer creates a new gRPC server.

type GRPCService

type GRPCService struct {
	// contains filtered or unexported fields
}

GRPCService wraps a GRPC server implementation and provides lifecycle management. It implements vessel's di.Service interface so Vessel can manage its lifecycle.

func NewGRPCService

func NewGRPCService(config Config, logger forge.Logger, metrics forge.Metrics) (*GRPCService, error)

NewGRPCService creates a new gRPC service with the given configuration. This is the constructor that will be registered with the DI container.

func (*GRPCService) AddStreamInterceptor

func (s *GRPCService) AddStreamInterceptor(interceptor grpc.StreamServerInterceptor)

func (*GRPCService) AddUnaryInterceptor

func (s *GRPCService) AddUnaryInterceptor(interceptor grpc.UnaryServerInterceptor)

func (*GRPCService) GetServer

func (s *GRPCService) GetServer() *grpc.Server

GetServer returns the underlying grpc.Server. It is nil until Start has built it, and registering a generated service on a serving server crashes the process. Use RegisterService before Start.

func (*GRPCService) GetServices

func (s *GRPCService) GetServices() []ServiceInfo

func (*GRPCService) GetStats

func (s *GRPCService) GetStats() ServerStats

func (*GRPCService) Health

func (s *GRPCService) Health(ctx context.Context) error

Health checks if the gRPC service is healthy.

func (*GRPCService) IsRunning

func (s *GRPCService) IsRunning() bool

func (*GRPCService) Name

func (s *GRPCService) Name() string

Name returns the service name for Vessel's lifecycle management.

func (*GRPCService) Ping

func (s *GRPCService) Ping(ctx context.Context) error

func (*GRPCService) RegisterHealthChecker

func (s *GRPCService) RegisterHealthChecker(service string, checker HealthChecker)

func (*GRPCService) RegisterService

func (s *GRPCService) RegisterService(desc *grpc.ServiceDesc, impl any) error

func (*GRPCService) Server

func (s *GRPCService) Server() GRPC

Server returns the underlying gRPC server implementation.

func (*GRPCService) Start

func (s *GRPCService) Start(ctx context.Context) error

Start starts the gRPC service. Vessel calls it the first time anything resolves the service. Under the extension it does nothing, because the extension starts the server at the end of app.Start, so services that other extensions and hooks register on the way still make it onto it.

func (*GRPCService) Stop

func (s *GRPCService) Stop(ctx context.Context) error

Stop stops the gRPC service gracefully. This is called automatically by Vessel during container.Stop().

type HealthChecker

type HealthChecker interface {
	Check(ctx context.Context) error
}

HealthChecker checks service health.

type KeepaliveConfig

type KeepaliveConfig struct {
	Time                time.Duration `json:"time"                  mapstructure:"time"                  yaml:"time"`
	Timeout             time.Duration `json:"timeout"               mapstructure:"timeout"               yaml:"timeout"`
	EnforcementPolicy   bool          `json:"enforcement_policy"    mapstructure:"enforcement_policy"    yaml:"enforcement_policy"`
	MinTime             time.Duration `json:"min_time"              mapstructure:"min_time"              yaml:"min_time"`
	PermitWithoutStream bool          `json:"permit_without_stream" mapstructure:"permit_without_stream" yaml:"permit_without_stream"`
}

KeepaliveConfig contains keepalive settings.

type MethodInfo

type MethodInfo struct {
	Name           string
	IsClientStream bool
	IsServerStream bool
	InputType      string
	OutputType     string
}

MethodInfo contains method metadata.

type ServerStats

type ServerStats struct {
	StartTime        int64
	TotalConnections int64
	ActiveStreams    int64
	RPCsStarted      int64
	RPCsSucceeded    int64
	RPCsFailed       int64
}

ServerStats contains server statistics.

type ServiceInfo

type ServiceInfo struct {
	Name        string
	Methods     []MethodInfo
	Description string
}

ServiceInfo contains service metadata.

type ServiceRegistrar added in v1.12.9

type ServiceRegistrar func(app forge.App, server GRPC) error

ServiceRegistrar sets up the extension's server before it starts. It can register services, add interceptors and register health checkers. app is the app the extension is registered with, so an implementation can resolve what it needs from app.Container().

The extension calls each registrar once, in order, when it builds the server. That happens the first time anything resolves the gRPC service, which is no later than the extension's Start. A registrar that resolves GRPC or *GRPCService, directly or through another constructor, gets a circular dependency error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL