clientgrpc

package module
v0.24.0 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 13 Imported by: 0

README

client-grpc

Outbound gRPC client for omcrgnt apps: ecfg catalog resource, plaintext dial to host:port, OpenTelemetry traces via otelgrpc, Prometheus client metrics via go-grpc-middleware/providers/prometheus.

Pair with srv-grpc on the server side.

Catalog

import clientgrpc "github.com/omcrgnt/client-grpc"

type catalog struct {
	AssetGRPC *clientgrpc.Client `ecfg:"ASSET_GRPC"`
}

Client depends on the shared *GRPCMetrics singleton (registered in unique.Global on import, like srv-grpc). Ops metrics actuator calls RegisterMetrics before StandBy.

Typed stubs are created by the app from Conn():

mapv1.NewMapServiceClient(c.AssetGRPC.Conn())

Environment

Prefix comes from the app (EnvPrefix); fields below are relative to the slot (e.g. ASSET_GRPC).

Variable Description
ASSET_GRPC_LABEL Resource label (otel attribute client)
ASSET_GRPC_HOST Target host / IP (common.v1.Host)
ASSET_GRPC_PORT Target port 1–65535 (common.v1.Port)

Example:

BEMVPGAME_ASSET_GRPC_LABEL=asset
BEMVPGAME_ASSET_GRPC_HOST=127.0.0.1
BEMVPGAME_ASSET_GRPC_PORT=9084

Lifecycle

Hook Role
Build / BuildConfig Materialize from ecfg (Label, Host, Port)
Deps / Inject Wire *GRPCMetrics
StandBy grpc.NewClient with otel stats handler + prometheus interceptors; returns a cleanup that closes ClientConn
Ready / ProbeReady Wait until connectivity Ready

StandBy, not runner.Starter — see Client.StandBy's doc comment for why. StandBy returns its own cleanup (func(context.Context) error) instead of a separately-implemented Close; runner.Runner retains and calls it during Stop — or immediately, to unwind, if a sibling resource's own StandBy or Start fails. See Client.StandBy's doc comment and runner's package doc for the full StandBy→Start ordering.

v1 transport is insecure/plaintext (internal mesh). TLS can be added later.

Telemetry

On StandBy:

  • otelgrpc.NewClientHandler — client spans/metrics to process TracerProvider
  • prometheus ClientMetrics (with handling-time histogram) — grpc_client_* series

Import the package (or srv-grpc) so metrics singletons exist; scrape via ops /metrics.

Example

See example/app.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

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

Client is an outbound gRPC connection resource. Catalog field: *Client (Configurable); dial happens in StandBy after Inject resolves GRPCMetrics.

func New added in v0.22.0

func New(opts ...Option) *Client

New constructs a Client with the given options applied. The catalog field holding it must be assigned this (non-nil) in the app's resources literal — app/fill.go's catalogCallable only preserves a Configurable's pre-constructed state through BuildConfig if the field isn't nil to begin with; left nil (the zero-value default), NewResource-equivalent handling creates a blank *Client and these options are lost. See pkg/atlasmigrate's doc comment for the same rule applied to a ResourceFactory instead of a Configurable.

func (*Client) BuildConfig

func (c *Client) BuildConfig() (app.Materializer, error)

BuildConfig returns the config spec for materialize, carrying over whatever options New was called with.

func (*Client) Conn

func (c *Client) Conn() *grpc.ClientConn

Conn returns the underlying gRPC connection (nil before StandBy).

func (*Client) Deps

func (*Client) Deps() []any

Deps declares the shared GRPCMetrics singleton.

func (*Client) Inject

func (c *Client) Inject(args []any)

Inject receives GRPCMetrics from SDI.

func (*Client) Label

func (c *Client) Label() string

Label returns the configured resource label.

func (*Client) ProbeReady

func (c *Client) ProbeReady(ctx context.Context) error

ProbeReady reports outbound gRPC readiness (ops duck typing; no ops import).

func (*Client) Ready

func (c *Client) Ready(ctx context.Context) error

Ready waits until the connection is Ready (or ctx ends).

func (*Client) StandBy added in v0.2.0

func (c *Client) StandBy() (func(context.Context) error, error)

StandBy dials the target with otel + prometheus client instrumentation. grpc.NewClient performs no I/O — it builds a ClientConn that connects lazily on first RPC — so this runs in runner.Runner's sequential StandBy phase rather than as a runner.Starter.

On success it returns a cleanup that closes conn — runner.Runner retains this closure and calls it during Stop (or immediately, to unwind, if a later sibling's own StandBy or Start fails). There is no started()-guard here: Runner only ever calls a cleanup it received from a StandBy call that itself succeeded, so a nil c.conn can't happen when this runs.

func (*Client) Target

func (c *Client) Target() string

Target returns host:port.

type Config

type Config struct {
	Label common.Label
	Host  common.Host
	Port  common.Port
	// contains filtered or unexported fields
}

Config is the gRPC client spec (Label, Host, Port); ecfg fills before Build. Target address is host:port (plaintext; TLS is out of scope for v1).

func (*Config) Build

func (cfg *Config) Build() (any, error)

type GRPCMetrics

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

GRPCMetrics is a singleton pool resource: contributor + shared prometheus interceptors for all client-grpc clients. RegisterMetrics matches ops metrics duck-typing (no ops import).

func (*GRPCMetrics) RegisterMetrics

func (m *GRPCMetrics) RegisterMetrics(reg *prom.Registry) error

func (*GRPCMetrics) StreamClientInterceptor

func (m *GRPCMetrics) StreamClientInterceptor() grpc.StreamClientInterceptor

func (*GRPCMetrics) UnaryClientInterceptor

func (m *GRPCMetrics) UnaryClientInterceptor() grpc.UnaryClientInterceptor

type Option added in v0.22.0

type Option func(*Client)

Option configures a Client at construction time, for values ecfg can't fill (e.g. interceptor functions) — see New.

func WithUnaryClientInterceptors added in v0.22.0

func WithUnaryClientInterceptors(in ...grpc.UnaryClientInterceptor) Option

WithUnaryClientInterceptors appends interceptors run in addition to (not instead of) the metrics interceptor this package always installs, in the order given, closest-to-the-call last (grpc.WithChainUnaryInterceptor semantics).

type Tagged added in v0.23.0

type Tagged[Tag any] struct {
	// contains filtered or unexported fields
}

Tagged wraps Client behind a phantom type parameter, letting one service hold several outbound gRPC clients to different targets at once. The org-framework's DI registry is type-unique (at most one entry per concrete Go type — see github.com/omcrgnt/res/unique's doc.go): two plain *Client catalog fields in the same resources struct collide at startup ("entry for type already has TagRegular"), no matter the distinct ecfg tag on each field — the tag only routes env vars into the pre-Build Config, it doesn't survive into the registry once both specs materialize into the same *Client type. Tagged[UserTag] and Tagged[CharacterTag] are distinct Go types even though structurally identical, so the registry holds both without collision — same trick srv-grpc's Server[T]/srv-http's Server[T] already use for handler types.

Usage: define an empty tag type per named client, e.g.

type UserTag struct{}
UserGRPC *clientgrpc.Tagged[UserTag] `ecfg:"USER"`

For a tagged client that also needs WithUnaryClientInterceptors (e.g. gctxgrpc propagation onward to a compat-filtering store — see npc-dialogue's shopstore client), use NewTagged/WithTaggedUnaryClientInterceptors below, same non-nil-catalog-field rule as Client's own New.

func NewTagged added in v0.24.0

func NewTagged[Tag any](opts ...TaggedOption[Tag]) *Tagged[Tag]

NewTagged constructs a Tagged[Tag] with the given options applied. The catalog field holding it must be assigned this (non-nil) — same catalogCallable rule as Client's own New (see its doc comment).

func (*Tagged[Tag]) BuildConfig added in v0.23.0

func (t *Tagged[Tag]) BuildConfig() (app.Materializer, error)

func (*Tagged[Tag]) Conn added in v0.23.0

func (c *Tagged[Tag]) Conn() *grpc.ClientConn

func (*Tagged[Tag]) Deps added in v0.23.0

func (c *Tagged[Tag]) Deps() []any

func (*Tagged[Tag]) Inject added in v0.23.0

func (c *Tagged[Tag]) Inject(args []any)

func (*Tagged[Tag]) Label added in v0.23.0

func (c *Tagged[Tag]) Label() string

func (*Tagged[Tag]) StandBy added in v0.23.0

func (c *Tagged[Tag]) StandBy() (func(context.Context) error, error)

func (*Tagged[Tag]) Target added in v0.23.0

func (c *Tagged[Tag]) Target() string

type TaggedOption added in v0.24.0

type TaggedOption[Tag any] func(*Tagged[Tag])

TaggedOption configures a Tagged[Tag] at construction time — same purpose as Client's own Option, for values ecfg can't fill.

func WithTaggedUnaryClientInterceptors added in v0.24.0

func WithTaggedUnaryClientInterceptors[Tag any](in ...grpc.UnaryClientInterceptor) TaggedOption[Tag]

WithTaggedUnaryClientInterceptors is Tagged's equivalent of WithUnaryClientInterceptors.

Directories

Path Synopsis
example
app command

Jump to

Keyboard shortcuts

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