rpcz

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2021 License: Apache-2.0 Imports: 16 Imported by: 2

README

rpcz

RPCz is a library for RPC-style interservice communication over network. It's simple, lightweight yet performant. It provides you with the infrastructure for building services and clients, taking care of the low level details such as connection handling and lifecycle management.

It's designed to work over plain TCP or Unix sockets, or with TLS on top of it. The primary encoding is Protobuf, with JSON available as an alternative. Adjustable bufferring helps achieve better throughput based on the needs of your application.

While providing similar functionality to gRPC, Twirp and the standard library's rpc package, RPCz differs from each in some way:

  • it's lightweight
  • it focuses on solving one particular task – performant communication over TCP
  • the public API is minimal, and consists primarily of a handful of convenience constructors
  • supports only two encodings
  • it handles connections gracefully, and supports (even enforces) use of context.Context.

The library code uses only one external dependency, Protobuf.

Examples

An example of a server running an RPC service and a client that interacts with it are available in this repository.

In Other Languages

The most obvious use case is communication between services developed in Go. However, one of the main reasons for creating this library was the need to interact with software written in different languages.

The simple protocol of RPCz and the use of Protobuf allow for such cross-language comminication. A server implementing the RPCz protocol must accept and correctly handle a request from a client implementing the protocol.

Language Server Client Status
Go Initial release
Rust Planned
Zig Planned

There are currently no plans on implementations for languages other than the listed above.

Benchmarks

The developer's machine has the following configuation:

goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz

The benchmark resutls have been obtained by running:

go test -benchmem -benchtime 5s -bench=Benchmark -count 5 -timeout 600s -cpu=8 | tee results.bench

benchstat results.bench

And here are the results:

name                time/op
Protobuf_1K-8         13.4µs ± 7%
Protobuf_4K-8         19.2µs ± 1%
Protobuf_16K-8        38.0µs ± 3%
Protobuf_32K-8        65.9µs ± 2%
Protobuf_64K-8        91.6µs ±14%
ProtobufTLS_4K-8      19.6µs ± 3%
ProtobufNoBuf_4K-8    25.9µs ± 5%
JSON_1K-8             20.4µs ± 4%
JSON_4K-8             53.6µs ± 5%
JSON_16K-8             185µs ± 2%
JSONTLS_4K-8          55.9µs ± 2%

name                speed
Protobuf_1K-8        153MB/s ± 7%
Protobuf_4K-8        427MB/s ± 1%
Protobuf_16K-8       862MB/s ± 3%
Protobuf_32K-8      1.00GB/s ± 2%
Protobuf_64K-8      1.44GB/s ±13%
ProtobufTLS_4K-8     417MB/s ± 3%
ProtobufNoBuf_4K-8   317MB/s ± 5%
JSON_1K-8            100MB/s ± 4%
JSON_4K-8            153MB/s ± 5%
JSON_16K-8           177MB/s ± 2%
JSONTLS_4K-8         147MB/s ± 2%

name                alloc/op
Protobuf_1K-8         9.42kB ± 0%
Protobuf_4K-8         40.1kB ± 0%
Protobuf_16K-8         155kB ± 0%
Protobuf_32K-8         333kB ± 0%
Protobuf_64K-8         614kB ± 0%
ProtobufTLS_4K-8      37.9kB ± 0%
ProtobufNoBuf_4K-8    47.6kB ± 0%
JSON_1K-8             5.56kB ± 0%
JSON_4K-8             19.2kB ± 0%
JSON_16K-8            72.3kB ± 0%
JSONTLS_4K-8          19.3kB ± 0%

name                allocs/op
Protobuf_1K-8           21.0 ± 0%
Protobuf_4K-8           21.0 ± 0%
Protobuf_16K-8          21.0 ± 0%
Protobuf_32K-8          21.0 ± 0%
Protobuf_64K-8          21.0 ± 0%
ProtobufTLS_4K-8        22.0 ± 0%
ProtobufNoBuf_4K-8      27.0 ± 0%
JSON_1K-8               29.0 ± 0%
JSON_4K-8               29.0 ± 0%
JSON_16K-8              29.0 ± 0%
JSONTLS_4K-8            30.0 ± 0%

You can also have a look here for benchmarks of other systems, such as gRPC and the rpc package from the standard library.

Open-Source, not Open-Contribution

Similar to SQLite and Litestream, RPCz is open-source, but is not open to contributions. This helps keep the code base free of confusions with licenses or proprietary changes, and prevent feature bloat.

In addition, experiences of many open-source projects have shown that maintenance of an open-source code base can be quite resource demanding. Time, mental health and energy – are just a few.

Taking the above into account, I've made the decision to make the project closed to contributions.

Thank you for understanding!

Documentation

Overview

Package rpcz allows exposing and accessing methods of a service over network.

This package supports communication over TCP, Unix sockets, and with TLS on top of it.
The default and recommended encoding is Protobuf, with JSON available as an alternative.
No user-defined encodings (aka codecs) are supported, nor it is planned.
It is as an alternative to rpc from the standard library and gRPC.

The server allows to register and expose one or more services. A service must be a pointer to a value of an exported type.
Each method meeting the following criteria will be registered and can be called remotely:

	- A method must be exported.
	- A method has three arguments.
	- The first argument is context.Context.
	- The second and third arguments are both pointers and exported.
	- A method has only one return parameter of the type error.

The following line illustrates a valid method's signature:

	func (s *Service) SomeMethod(ctx context.Context, req *SomeMethodRequest, resp *SomeMethodResponse) error

The request and response types must be marshallable to Protobuf, i.e. implement the proto.Message interface.

The first argument of a service's method is passed in the server's context. This context is cancelled when
the server is requested to shutdown. The server is not concerned with specifying timeouts for service's methods.
It's the service's responsibility to implement timeouts should they be needed. The primary use of the parent context
is to be notified when shutdown has been requested to gracefully finish any ongoing operations.

At the moment, the context does not include any data, but it might be exteneded at a later point with useful information
such as a request trace identificator. Reminder: contexts MUST NOT be used for dependency injection.

The second argument represents a request to the method of a service. The third argument is passed in a pointer to a value
to which the method writes the response.

If a method returns an error, it's sent over the wire as a string, and when the client returns it as ServiceError.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidEncoding      = errors.New("rpcz: invalid encoding")
	ErrInvalidServerOptions = errors.New("rpcz: invalid server options: must not be nil")

	ErrInvalidAddr     = errors.New("rpcz: invalid server address")
	ErrInvalidListener = errors.New("rpcz: invalid server listener")
	ErrSrvStarted      = errors.New("rpcz: server already started")
	ErrSrvClosed       = errors.New("rpcz: server already shutdown")

	ErrInvalidClientOptions = errors.New("rpcz: invalid client options: must not be nil")
	ErrShutdown             = errors.New("rpcz: client is shutdown")
	ErrClosed               = errors.New("rpcz: client is closed")
)

Functions

This section is empty.

Types

type Client

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

Client makes requests to remote methods on services registered on a remote server.

func NewClient

func NewClient(saddr string) (*Client, error)

NewClient returns a client connected to saddr with Protobuf encoding and default options.

func NewClientTLS

func NewClientTLS(saddr string, cfg *tls.Config) (*Client, error)

NewClientTLS returns a client connected to saddr in TLS mode.

func NewClientTLSOptions

func NewClientTLSOptions(saddr string, cfg *tls.Config, opts *ClientOptions) (*Client, error)

NewClientTLSOptions creates a client connected to saddr over TLS with opts.

func NewClientWithConn

func NewClientWithConn(opts *ClientOptions, nc net.Conn) (*Client, error)

NewClientWithConn creates a client set up with opts connected to nc.

func NewClientWithOptions

func NewClientWithOptions(saddr string, opts *ClientOptions) (*Client, error)

NewClientWithOptions returns a client connected to saddr and set up with opts.

func (*Client) Close

func (c *Client) Close() error

Close closes the connection and shuts down the client.

Any unfinished requests are aborted.

func (*Client) Do

func (c *Client) Do(ctx context.Context, svc, mtd string, args, resp interface{}) *Result

Do asynchronously calls mtd on svc with given args and resp.

func (*Client) Peer

func (c *Client) Peer() string

Peer returns the address of the server c is connected to.

func (*Client) SyncDo

func (c *Client) SyncDo(ctx context.Context, svc, mtd string, args, resp interface{}) error

SyncDo calls Do and awaits for the response.

type ClientOptions

type ClientOptions struct {
	Encoding     Encoding
	ReadBufSize  int
	WriteBufSize int
}

ClientOptions allows to setup Client.

An empty value of ClientOptions uses safe defaults: - Protobuf encoding - 8192 bytes per connection for a read buffer - 8192 bytes per connection for a write buffer.

func (*ClientOptions) Copy

func (o *ClientOptions) Copy() *ClientOptions

Copy returns a full copy of o.

type Encoding

type Encoding int8

Encoding specifies which encoding is supported by a concrete server, client, and transport.

const (
	// Supported encodings.
	Unknown Encoding = -1 + iota
	Protobuf
	JSON
)

type Result

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

Result represents a result of a request.

func (*Result) Err

func (r *Result) Err() error

Err waits for the request to finish and returns nil on success, and an error otherwise.

func (*Result) ErrChan

func (r *Result) ErrChan() <-chan error

ErrChan returns a channel that indicates completion of the request by sending a nil or non-nil error.

type Server

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

Server handles requests from clients by calling methods on registered services.

func NewServer

func NewServer(laddr string) (*Server, error)

NewServer returns a server listening on laddr with Protobuf encoding and default options.

func NewServerTLS

func NewServerTLS(laddr string, cfg *tls.Config) (*Server, error)

NewServerTLS returns a server listening on laddr in TLS mode.

func NewServerTLSOptions

func NewServerTLSOptions(laddr string, cfg *tls.Config, opts *ServerOptions) (*Server, error)

NewServerTLSOptions creates a server listening on laddr in TLS mode with opts.

func NewServerWithListener

func NewServerWithListener(opts *ServerOptions, ln net.Listener) (*Server, error)

NewServerWithListener returns a server set up with opts listening on ln.

func NewServerWithOptions

func NewServerWithOptions(laddr string, opts *ServerOptions) (*Server, error)

NewServerWithOptions returns a server listening on laddr and set up with opts.

func (*Server) Register

func (s *Server) Register(svc interface{}) error

Register registers the given svc.

The svc is registered if it's exported and at least one method satisfies the following conditions:

  • a method is exported
  • a method has three arguments
  • the first argument is context.Context
  • the second and third arguments are both pointers to exported values that implement proto.Message
  • a method has only one return parameter of the type error.

New services can be added while the server is runnning.

func (*Server) Run

func (s *Server) Run(ctx context.Context) error

Run is an alias for Start.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server. A successful call returns no error.

It iteratively tries to close new and idle connections until no such left, unless/until interrupted by cancellation of ctx. Subsequent calls return ErrSrvClosed.

func (*Server) Start

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

Start starts accepting connections.

After a successful call to Start, subsequent calls to return ErrServerStarted.

func (*Server) Started

func (s *Server) Started() bool

Started returns true if the server is started.

type ServerOptions

type ServerOptions struct {
	Encoding     Encoding
	ReadBufSize  int
	WriteBufSize int
}

ServerOptions allows to setup Server.

An empty value of ServerOptions uses safe defaults: - Protobuf encoding - 8192 bytes per connection for a read buffer - 8192 bytes per connection for a write buffer.

func (*ServerOptions) Copy

func (o *ServerOptions) Copy() *ServerOptions

Copy returns a full copy of o.

type ServiceError

type ServiceError string

ServiceError represents an error returned by a remote method.

func (ServiceError) Error

func (e ServiceError) Error() string

Error returns the string representation of e.

Directories

Path Synopsis
internal
echo
Package echo shows a simple RPC service that can be served with rpcz.
Package echo shows a simple RPC service that can be served with rpcz.

Jump to

Keyboard shortcuts

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