or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

admin.mdadvanced.mdclient-server.mdcredentials-security.mderrors-status.mdhealth.mdindex.mdinterceptors.mdload-balancing.mdmetadata-context.mdname-resolution.mdobservability.mdreflection.mdstreaming.mdtesting.mdxds.md
tile.json

client-server.mddocs/

Client-Server Core

This document covers the core client and server functionality in gRPC-Go, including client connection management, server creation and lifecycle, dial options, server options, and service registration.

Client Connection

ClientConn

ClientConn represents a virtual connection to a conceptual endpoint. It manages name resolution, load balancing, connection pooling, and RPC invocation.

type ClientConn struct {
    // Has unexported fields
}

Creating Clients

// NewClient creates a new gRPC client connection (recommended)
func NewClient(target string, opts ...DialOption) (*ClientConn, error)

// Deprecated: Use NewClient instead
func Dial(target string, opts ...DialOption) (*ClientConn, error)
func DialContext(ctx context.Context, target string, opts ...DialOption) (*ClientConn, error)

Example:

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

// Basic connection
conn, err := grpc.NewClient("localhost:50051",
    grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
    log.Fatalf("failed to connect: %v", err)
}
defer conn.Close()

// With TLS
creds, _ := credentials.NewClientTLSFromFile("ca.pem", "")
conn, err := grpc.NewClient("example.com:443",
    grpc.WithTransportCredentials(creds))

// With multiple options
conn, err := grpc.NewClient("dns:///myservice:8080",
    grpc.WithTransportCredentials(creds),
    grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`),
    grpc.WithKeepaliveParams(keepalive.ClientParameters{
        Time:    10 * time.Second,
        Timeout: 3 * time.Second,
    }))

ClientConn Methods

// Invoke performs a unary RPC
func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply any, opts ...CallOption) error

// NewStream creates a new stream for streaming RPCs
func (cc *ClientConn) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error)

// Close tears down the ClientConn and all underlying connections
func (cc *ClientConn) Close() error

// GetState returns the connectivity state
func (cc *ClientConn) GetState() connectivity.State

// WaitForStateChange waits until connectivity state changes
func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool

// Connect causes all subchannels to attempt to connect (experimental)
func (cc *ClientConn) Connect()

// ResetConnectBackoff resets backoff timers and causes immediate reconnect attempts (experimental)
func (cc *ClientConn) ResetConnectBackoff()

// Target returns the target string
func (cc *ClientConn) Target() string

// CanonicalTarget returns the canonical target string
func (cc *ClientConn) CanonicalTarget() string

// GetMethodConfig gets the method configuration for a specific method
func (cc *ClientConn) GetMethodConfig(method string) MethodConfig

ClientConnInterface

Generated code uses this interface, allowing for easier testing and mocking.

type ClientConnInterface interface {
    Invoke(ctx context.Context, method string, args any, reply any, opts ...CallOption) error
    NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error)
}

Dial Options

Dial options configure the client connection behavior.

Core Dial Options

type DialOption interface {
    // Has unexported methods
}

// Credentials
func WithTransportCredentials(creds credentials.TransportCredentials) DialOption
func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption
func WithCredentialsBundle(b credentials.Bundle) DialOption // Experimental

// Insecure (for testing only)
func WithInsecure() DialOption // Deprecated: use WithTransportCredentials(insecure.NewCredentials())

// Connection parameters
func WithConnectParams(p ConnectParams) DialOption
func WithBackoffConfig(b BackoffConfig) DialOption // Deprecated
func WithBackoffMaxDelay(md time.Duration) DialOption // Deprecated

// Timeouts and idle
func WithIdleTimeout(d time.Duration) DialOption // Experimental
func WithTimeout(d time.Duration) DialOption // Deprecated
func WithBlock() DialOption // Deprecated

// Authority and naming
func WithAuthority(a string) DialOption

// Service config
func WithDefaultServiceConfig(s string) DialOption
func WithDisableServiceConfig() DialOption

// Resolvers and balancers
func WithResolvers(rs ...resolver.Builder) DialOption // Experimental
func WithDefaultCallOptions(cos ...CallOption) DialOption

// Interceptors
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption
func WithStreamInterceptor(f StreamClientInterceptor) DialOption
func WithChainUnaryInterceptor(interceptors ...UnaryClientInterceptor) DialOption
func WithChainStreamInterceptor(interceptors ...StreamClientInterceptor) DialOption

// Stats and observability
func WithStatsHandler(h stats.Handler) DialOption
func WithChannelzParentID(c channelz.Identifier) DialOption // Experimental

// Message size limits
func WithMaxMsgSize(s int) DialOption // Deprecated: use WithDefaultCallOptions(MaxCallRecvMsgSize(s))
func WithDefaultCallOptions(cos ...CallOption) DialOption
// Use with MaxCallRecvMsgSize(bytes int) and MaxCallSendMsgSize(bytes int)

// Header sizes
func WithMaxHeaderListSize(s uint32) DialOption

// Window sizes and flow control
func WithInitialWindowSize(s int32) DialOption
func WithInitialConnWindowSize(s int32) DialOption
func WithStaticStreamWindowSize(s int32) DialOption
func WithStaticConnWindowSize(s int32) DialOption

// Buffer sizes
func WithReadBufferSize(s int) DialOption
func WithWriteBufferSize(s int) DialOption
func WithSharedWriteBuffer(val bool) DialOption // Experimental

// Keepalive
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption

// Dialer customization
func WithContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption
func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption // Deprecated

// Proxy configuration
func WithNoProxy() DialOption // Experimental
func WithLocalDNSResolution() DialOption // Experimental

// Retry and error handling
func WithDisableRetry() DialOption
func WithMaxCallAttempts(n int) DialOption
func WithDisableHealthCheck() DialOption // Experimental
func FailOnNonTempDialError(f bool) DialOption // Deprecated
func WithReturnConnectionError() DialOption // Deprecated

// Codec (deprecated - use encoding package)
func WithCodec(c Codec) DialOption // Deprecated
func WithCompressor(cp Compressor) DialOption // Deprecated
func WithDecompressor(dc Decompressor) DialOption // Deprecated

// User agent
func WithUserAgent(s string) DialOption

ConnectParams

type ConnectParams struct {
    // Backoff specifies the configuration options for connection backoff
    Backoff backoff.Config
    // MinConnectTimeout is the minimum amount of time we are willing to give a connection to complete
    MinConnectTimeout time.Duration
}

Example:

import "google.golang.org/grpc/backoff"

conn, err := grpc.NewClient("localhost:50051",
    grpc.WithTransportCredentials(insecure.NewCredentials()),
    grpc.WithConnectParams(grpc.ConnectParams{
        Backoff: backoff.Config{
            BaseDelay:  1.0 * time.Second,
            Multiplier: 1.6,
            Jitter:     0.2,
            MaxDelay:   120 * time.Second,
        },
        MinConnectTimeout: 5 * time.Second,
    }))

BackoffConfig (Deprecated)

type BackoffConfig struct {
    MaxDelay time.Duration
}

var DefaultBackoffConfig = BackoffConfig{
    MaxDelay: 120 * time.Second,
}

EmptyDialOption

type EmptyDialOption struct{}

Embeddable type for creating custom dial options.

Server

Server Type

type Server struct {
    // Has unexported fields
}

Creating Servers

// NewServer creates a gRPC server with the specified options
func NewServer(opt ...ServerOption) *Server

Example:

import (
    "net"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
)

// Basic server
s := grpc.NewServer()

// With TLS
creds, _ := credentials.NewServerTLSFromFile("server.crt", "server.key")
s := grpc.NewServer(grpc.Creds(creds))

// With multiple options
s := grpc.NewServer(
    grpc.Creds(creds),
    grpc.MaxConcurrentStreams(100),
    grpc.KeepaliveParams(keepalive.ServerParameters{
        MaxConnectionIdle: 5 * time.Minute,
        Time:              10 * time.Second,
        Timeout:           3 * time.Second,
    }),
    grpc.ChainUnaryInterceptor(loggingInterceptor, authInterceptor),
    grpc.ChainStreamInterceptor(streamLoggingInterceptor),
)

Server Methods

// RegisterService registers a service and its implementation
func (s *Server) RegisterService(sd *ServiceDesc, ss any)

// Serve accepts incoming connections on the listener
func (s *Server) Serve(lis net.Listener) error

// ServeHTTP implements http.Handler for HTTP/2 multiplexing (experimental)
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

// GracefulStop gracefully stops the server
func (s *Server) GracefulStop()

// Stop immediately stops the server
func (s *Server) Stop()

// GetServiceInfo returns service information for all registered services
func (s *Server) GetServiceInfo() map[string]ServiceInfo

Example:

// Start server
lis, err := net.Listen("tcp", ":50051")
if err != nil {
    log.Fatalf("failed to listen: %v", err)
}

s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})

// Start serving
if err := s.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %v", err)
}

// Graceful shutdown
go func() {
    <-ctx.Done()
    s.GracefulStop()
}()

Server Options

Server options configure server behavior.

type ServerOption interface {
    // Has unexported methods
}

// Credentials
func Creds(c credentials.TransportCredentials) ServerOption

// Interceptors
func UnaryInterceptor(i UnaryServerInterceptor) ServerOption
func StreamInterceptor(i StreamServerInterceptor) ServerOption
func ChainUnaryInterceptor(interceptors ...UnaryServerInterceptor) ServerOption
func ChainStreamInterceptor(interceptors ...StreamServerInterceptor) ServerOption

// Message size limits
func MaxMsgSize(m int) ServerOption // Deprecated: use MaxRecvMsgSize
func MaxRecvMsgSize(m int) ServerOption
func MaxSendMsgSize(m int) ServerOption

// Header sizes
func MaxHeaderListSize(s uint32) ServerOption
func HeaderTableSize(s uint32) ServerOption // Experimental

// Connection limits
func MaxConcurrentStreams(n uint32) ServerOption

// Connection timeout
func ConnectionTimeout(d time.Duration) ServerOption

// Keepalive
func KeepaliveParams(kp keepalive.ServerParameters) ServerOption
func KeepaliveEnforcementPolicy(kep keepalive.EnforcementPolicy) ServerOption

// Window sizes and flow control
func InitialWindowSize(s int32) ServerOption
func InitialConnWindowSize(s int32) ServerOption
func StaticStreamWindowSize(s int32) ServerOption
func StaticConnWindowSize(s int32) ServerOption

// Buffer sizes
func ReadBufferSize(s int) ServerOption
func WriteBufferSize(s int) ServerOption
func SharedWriteBuffer(val bool) ServerOption // Experimental

// Worker configuration
func NumStreamWorkers(numServerWorkers uint32) ServerOption // Experimental

// Stats and observability
func StatsHandler(h stats.Handler) ServerOption
func InTapHandle(h tap.ServerInHandle) ServerOption // Experimental

// Unknown service handler
func UnknownServiceHandler(streamHandler StreamHandler) ServerOption

// Shutdown behavior
func WaitForHandlers(w bool) ServerOption // Experimental

// Codec (deprecated - use encoding package)
func CustomCodec(codec Codec) ServerOption // Deprecated
func ForceServerCodec(codec encoding.Codec) ServerOption // Deprecated
func ForceServerCodecV2(codecV2 encoding.CodecV2) ServerOption // Experimental

// Compression (deprecated - use encoding package)
func RPCCompressor(cp Compressor) ServerOption // Deprecated
func RPCDecompressor(dc Decompressor) ServerOption // Deprecated

EmptyServerOption

type EmptyServerOption struct{}

Embeddable type for creating custom server options.

Service Registration

ServiceDesc

type ServiceDesc struct {
    ServiceName string
    // The pointer to the service interface
    HandlerType any
    Methods     []MethodDesc
    Streams     []StreamDesc
    Metadata    any
}

MethodDesc

type MethodDesc struct {
    MethodName string
    Handler    MethodHandler
}

type MethodHandler func(srv any, ctx context.Context, dec func(any) error, interceptor UnaryServerInterceptor) (any, error)

ServiceInfo

type ServiceInfo struct {
    Methods  []MethodInfo
    Metadata any
}

type MethodInfo struct {
    Name           string
    IsClientStream bool
    IsServerStream bool
}

ServiceRegistrar

type ServiceRegistrar interface {
    RegisterService(desc *ServiceDesc, impl any)
}

This interface allows service registration with types other than *grpc.Server.

Call Options

Call options configure individual RPC calls.

type CallOption interface {
    // Has unexported methods
}

// Metadata
func Header(md *metadata.MD) CallOption
func Trailer(md *metadata.MD) CallOption

// Peer information
func Peer(p *peer.Peer) CallOption

// Credentials
func PerRPCCredentials(creds credentials.PerRPCCredentials) CallOption

// Message size
func MaxCallRecvMsgSize(bytes int) CallOption
func MaxCallSendMsgSize(bytes int) CallOption

// Retry
func MaxRetryRPCBufferSize(bytes int) CallOption // Experimental
func WaitForReady(waitForReady bool) CallOption
func FailFast(failFast bool) CallOption // Deprecated: use WaitForReady

// Codec and compression
func ForceCodec(codec encoding.Codec) CallOption // Experimental
func ForceCodecV2(codec encoding.CodecV2) CallOption // Experimental
func CallContentSubtype(contentSubtype string) CallOption
func UseCompressor(name string) CallOption // Experimental
func CallCustomCodec(codec Codec) CallOption // Deprecated

// Authority
func CallAuthority(authority string) CallOption // Experimental

// Callbacks
func OnFinish(onFinish func(err error)) CallOption // Experimental

// Static method marker
func StaticMethod() CallOption

Call Option Struct Types

For advanced use, these struct types expose call option internals:

type HeaderCallOption struct {
    HeaderAddr *metadata.MD
}

type TrailerCallOption struct {
    TrailerAddr *metadata.MD
}

type PeerCallOption struct {
    PeerAddr *peer.Peer
}

type PerRPCCredsCallOption struct {
    Creds credentials.PerRPCCredentials
}

type MaxRecvMsgSizeCallOption struct {
    MaxRecvMsgSize int
}

type MaxSendMsgSizeCallOption struct {
    MaxSendMsgSize int
}

type MaxRetryRPCBufferSizeCallOption struct {
    MaxRetryRPCBufferSize int
}

type CompressorCallOption struct {
    CompressorType string
}

type ContentSubtypeCallOption struct {
    ContentSubtype string
}

type ForceCodecCallOption struct {
    Codec encoding.Codec
}

type ForceCodecV2CallOption struct {
    CodecV2 encoding.CodecV2
}

type CustomCodecCallOption struct {
    Codec Codec
}

type FailFastCallOption struct {
    FailFast bool
}

type OnFinishCallOption struct {
    OnFinish func(error)
}

type AuthorityOverrideCallOption struct {
    Authority string
}

type StaticMethodCallOption struct {
    EmptyCallOption
}

type EmptyCallOption struct{}

Connectivity State

// From google.golang.org/grpc/connectivity package

type State int

const (
    Idle State = iota
    Connecting
    Ready
    TransientFailure
    Shutdown
)

func (s State) String() string

Example:

import "google.golang.org/grpc/connectivity"

state := conn.GetState()
if state == connectivity.Ready {
    log.Println("Connection is ready")
}

// Wait for state change
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if conn.WaitForStateChange(ctx, connectivity.Connecting) {
    log.Println("State changed from Connecting")
}

Helper Functions

// Create a new client stream (wrapper for ClientConn.NewStream)
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error)

// Invoke a unary RPC (deprecated - use ClientConn.Invoke)
func Invoke(ctx context.Context, method string, args, reply any, cc *ClientConn, opts ...CallOption) error

Message Optimization

PreparedMsg

PreparedMsg is a type for optimizing message encoding when sending the same message multiple times. It pre-encodes a message so it can be efficiently sent to multiple RPCs without re-encoding.

type PreparedMsg struct {
    // Has unexported fields
}

// Encode marshals and compresses the message using the codec and compressor for the given stream
func (p *PreparedMsg) Encode(s Stream, msg any) error

Use Case: When broadcasting the same message to multiple clients or streams, encoding once and reusing improves performance.

Example:

import "google.golang.org/grpc"

// Prepare a message for efficient reuse
var preparedMsg grpc.PreparedMsg
err := preparedMsg.Encode(stream, message)
if err != nil {
    log.Fatalf("failed to encode: %v", err)
}

// Send to multiple streams efficiently
// The encoded message is reused internally

Version Information

const Version = "1.77.0"

// Support package version constants
const (
    SupportPackageIsVersion3 = true
    SupportPackageIsVersion4 = true
    SupportPackageIsVersion5 = true
    SupportPackageIsVersion6 = true
    SupportPackageIsVersion7 = true
    SupportPackageIsVersion8 = true
    SupportPackageIsVersion9 = true
)

Errors

var ErrServerStopped = errors.New("grpc: the server has been stopped")

var ErrClientConnClosing = status.Error(codes.Canceled, "grpc: the client connection is closing") // Deprecated

var ErrClientConnTimeout = errors.New("grpc: timed out when dialing") // Deprecated

Target URI Format

gRPC-Go uses URI syntax for specifying targets:

scheme://[authority]/endpoint

Examples:

  • dns:///example.com:443 - DNS resolution
  • dns://8.8.8.8/example.com:443 - DNS resolution with custom server
  • unix:///path/to/socket - Unix domain socket
  • passthrough:///localhost:50051 - Direct connection without name resolution
  • xds:///myservice - xDS-based service discovery

The default resolver is dns (can be changed via resolver.SetDefaultScheme).

Best Practices

Client

  1. Reuse connections: Create one ClientConn per target and reuse it for multiple RPCs
  2. Always close: Use defer conn.Close() to ensure cleanup
  3. Use context deadlines: Always set appropriate deadlines on RPC contexts
  4. Handle connectivity states: Monitor connection state for health checks
  5. Configure retry policies: Use service config for automatic retries
  6. Set reasonable timeouts: Configure idle timeout and keepalive parameters

Server

  1. Graceful shutdown: Always use GracefulStop() to allow in-flight RPCs to complete
  2. Set resource limits: Configure MaxConcurrentStreams and message size limits
  3. Use interceptors for cross-cutting concerns: Logging, auth, metrics
  4. Register services before Serve: All services must be registered before calling Serve()
  5. Handle signals: Listen for OS signals to trigger graceful shutdown
  6. Configure keepalive: Set appropriate keepalive parameters for long-lived connections

Example - Complete Server with Graceful Shutdown:

package main

import (
    "context"
    "log"
    "net"
    "os"
    "os/signal"
    "syscall"
    "time"

    "google.golang.org/grpc"
    "google.golang.org/grpc/keepalive"
)

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    s := grpc.NewServer(
        grpc.MaxConcurrentStreams(100),
        grpc.KeepaliveParams(keepalive.ServerParameters{
            MaxConnectionIdle: 5 * time.Minute,
            Time:              10 * time.Second,
            Timeout:           3 * time.Second,
        }),
        grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
            MinTime:             5 * time.Second,
            PermitWithoutStream: true,
        }),
    )

    // Register services
    // pb.RegisterYourServiceServer(s, &yourService{})

    // Handle graceful shutdown
    go func() {
        sigint := make(chan os.Signal, 1)
        signal.Notify(sigint, os.Interrupt, syscall.SIGTERM)
        <-sigint

        log.Println("Shutting down server...")
        s.GracefulStop()
    }()

    log.Println("Server listening on :50051")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}