brpc

package module
v0.0.0-...-6dd2113 Latest Latest
Warning

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

Go to latest
Published: Nov 30, 2023 License: MIT Imports: 21 Imported by: 3

README

brpc

A low-invasive, bidirectional gRPC framework for Go. This library is a proof of concept that allows your clients to connect to a gRPC server, and then expose a gRPC server of their own, allowing the real gRPC server to call RPCs on the client.

func (s *GreeterService) Greet(ctx context.Context, _ *example.GreetRequest) (*example.GreetResponse, error) {
	// The client provides a gRPC service. Let's extract it from the context.
	client, err := s.ClientFromContext(ctx)
	if err != nil {
		return nil, err
	}
	// Let's call the client's Name method, an RPC on the client's gRPC service.
	res, err := client.Name(ctx, &example.NameRequest{})
	if err != nil {
		return nil, err
	}
	return &example.GreetResponse{
		Greeting: fmt.Sprintf("Hello %v", res.GetName()),
	}, nil
}

Features

  • Bidirectional - Clients can expose a gRPC server of their own, allowing the real gRPC server to call RPCs on the client.
  • Low-invasive - Takes advantage of all the generated types and functions from protoc, you just need to plug everything into brpc.
  • Single connection - All connections are multiplexed across a single QUIC connection.
  • Go generics - Uses Go generics to make it easy to plug everything together correctly.

Internals

This library uses a single QUIC connection and all other connections are multiplexed across this connection. Clients receive connection IDs from the server which they then provide with every subsequent client-to-server RPC request, and the brpc server exposes the client's RPC methods inside your gRPC service so that you can call them from the server.

Example

See EXAMPLE.md for a full example.

Documentation

Index

Constants

View Source
const (
	ErrorCodeCreatingYamuxClient = iota + 1
	ErrorCodeOpeningGrpcConnection
)

Variables

View Source
var DefaultDialer net.Dialer
View Source
var (
	ErrClientNotConnected = errors.New("client not connected")
)

Functions

func ServeClientService

func ServeClientService[C any](shutdown <-chan struct{}, c *ClientConn, register ServiceRegisterFunc[C]) error

Types

type ClientConn

type ClientConn struct {
	Dialer func(ctx context.Context, target string) (quic.Connection, error)
	*grpc.ClientConn
	// contains filtered or unexported fields
}

ClientConn is a bidirectional gRPC connection that is generic over S, the gRPC server that we're connecting to. Callers use this connection to

  1. Serve a gRPC server that is accessible to a brpc server.
  2. Construct a gRPC client that can call the gRPC server.

func Dial

func Dial(target string, config *tls.Config) (*ClientConn, error)

func DialContext

func DialContext(ctx context.Context, target string, config *tls.Config) (*ClientConn, error)

func (*ClientConn) Close

func (c *ClientConn) Close() error

func (*ClientConn) WithStreamConnectionIdentifier

func (c *ClientConn) WithStreamConnectionIdentifier() grpc.DialOption

WithStreamConnectionIdentifier is a grpc.DialOption that adds the client's UUID to all stream requests. This is required if the server intends to call back to the client's gRPC server.

func (*ClientConn) WithUnaryConnectionIdentifier

func (c *ClientConn) WithUnaryConnectionIdentifier() grpc.DialOption

WithUnaryConnectionIdentifier is a grpc.DialOption that adds the client's UUID to all unary requests. This is required if the server intends to call back to the client's gRPC server.

type ErrYamuxNegotiationFailed

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

func (ErrYamuxNegotiationFailed) Error

type Server

type Server[C any] struct {
	Logger *slog.Logger
	*grpc.Server
	// contains filtered or unexported fields
}

Server is a bidirectional gRPC server that allows you to plug in your own gRPC server, as well as a gRPC client which your gRPC server can use to call client RPCs.

Server works by handling the initial connection negotiation, and then multiplexes all future communication, including client->server RPCs and server->client RPCs over a single TCP connection.

func NewServer

func NewServer[C any](config ServerConfig[C]) *Server[C]

NewServer constructs

func (*Server[C]) ClientFromContext

func (s *Server[C]) ClientFromContext(ctx context.Context) (client C, err error)

ClientFromContext returns a client

func (*Server[C]) GracefulStop

func (s *Server[C]) GracefulStop()

func (*Server[C]) Serve

func (s *Server[C]) Serve(ctx context.Context, listener *quic.Listener) error

type ServerConfig

type ServerConfig[C any] struct {
	// ClientServiceBuilder is a function that provides a grpc.ClientConnInterface so that
	// you can then construct your gRPC client. This client allows you to call methods on
	// your client, as if your client were a server. If I had generated a client called
	// "ClientService" then I can pass the generated gRPC constructor directly.
	//
	// 		ClientServiceBuilder: example.NewClientServiceClient
	//
	ClientServiceBuilder func(cc grpc.ClientConnInterface) C

	// The gRPC server that we should forward RPC requests to
	Server *grpc.Server
}

ServerConfig allows you to configure the server. It is generic over S (the gRPC service interface) and C (the gRPC client interface). When building a brpc server, you'll need to provide these builders for the server and client services.

type ServiceRegisterFunc

type ServiceRegisterFunc[Service any] func(registrar grpc.ServiceRegistrar)

ServiceRegisterFunc is a function responsible for registering a gRPC service that is served by a brpc client, for a brpc server. Clients must provide one of these when dialing a brpc server.

Directories

Path Synopsis
cmd
internal

Jump to

Keyboard shortcuts

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