package
0.0.0-20250914193024-d72055214c86
Repository: https://github.com/bryk-io/pkg.git
Documentation: pkg.go.dev

# Packages

No description provided by the author

# README

gRPC Utilities

RPC stands for Remote Procedure Call, and it's an architecture style for distributed systems in which a system causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a network). This package use gRPC as its underlying high-performance framework.

Deploying a gRPC service in production involves several aspects and considerations. Things like: logging, authentication, secure communication channels, request tracing, manage server resources, support regular HTTP (REST) access, etc. This package greatly simplifies the process of properly configuring and running a production grade RPC service.

More information: https://grpc.io/

Server

For the server implementation the main component is using a Server instance. The server is configured, using functional style parameters, by providing a list of options to the NewServer and/or Setup methods.

For example, let's create and start a server using some common configuration options.

// Server configuration options.
settings := []ServerOption{
WithLogger(nil),
WithPanicRecovery(),
WithServiceProvider(yourServiceHandler),
WithResourceLimits(ResourceLimits{
  Connections: 100,
  Requests:    100,
  Rate:        1000,
}),
}

// Create new server.
server, _ := NewServer(settings...)

// Start the server instance and wait for it to be ready.
ready := make(chan bool)
go server.Start(ready)
<-ready

// Server is ready now

Services

The most important configuration setting for a server instance are the "Services" it supports. You can provide services by implementing the ServiceProvider interface in your application and passing it along using the WithServiceProvider option.

// Echo service provider (i.e., implementing the "ServiceProvider" interface.)
type echoProvider struct{}

func (ep *echoProvider) ServerSetup(server*grpc.Server) {
  sampleV1.RegisterEchoAPIServer(server, &sampleV1.EchoHandler{})
}

func (ep *echoProvider) GatewaySetup() GatewayRegister {
  return sampleV1.RegisterEchoAPIHandler
}

// Base server configuration options
serverOpts := []ServerOption{
  WithPanicRecovery(),
  WithServiceProvider(&echoProvider{}),
}

Client

In order to interact with an RPC server and access the provided functionality you need to set up and establish a client connection. A client connection should be closed when no longer needed to free the used resources. A connection can also be monitored to detect any changes in its current state.

A connection could be obtained from a client instance. The benefit of this approach is that a single client instance can be used to generate multiple connections to different servers.

// client options
options := []ClientOption{
  WaitForReady(),
  WithTimeout(5 * time.Second),
}
client, err := NewClient(options...)
if err != nil {
  panic(err)
}

// Use client to get a connection
conn, err := client.GetConnection("server.com:9090")
if err != nil {
  panic(err)
}

// Use connection

// Close it when not needed any more
defer conn.Close()

For simpler use cases a connection can be directly obtained using the NewClientConnection method.

// client options
options := []ClientOption{
  WaitForReady(),
  WithTimeout(1 * time.Second),
}

// Get connection
conn, err := NewClientConnection("server.com:9090", options...)
if err != nil {
  panic(err)
}

// Use connection

// Close it when not needed any more
defer conn.Close()

Regardless of how a connection is created, you can set up a monitor for it using the MonitorClientConnection method. The monitor instance can be properly terminated using the provided context.

// Get a monitor instance with a 5 seconds check interval
ctx, close := context.WithCancel(context.Background())
defer close()
monitor := MonitorClientConnection(ctx, conn, 5*time.Second)

// Close the monitor in the background after 15 seconds
go func() {
  <-time.After(15*time.Second)
  close()
}

// Catch changes in the connection state
for state := range monitor {
  fmt.Printf("connection state: %s", state)
}

For more information about functional style configuration options check the original article by Dave Cheney: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis.