cmd-stream-go is a high-performance, modular client–server library for Go, based on the Command Pattern. It's designed for efficient, low-latency communication over TCP/TLS, with support for streaming and observability.
In short, the concept is simple: a client sends Commands to the server, where an Invoker executes them, and a Receiver provides the actual server-side functionality.
Want to learn more about how the Command Pattern applies to network communication? Check out this series of posts.
It delivers high-performance and resource efficiency, helping reduce infrastructure costs and scale more effectively.
- Works over TCP, TLS or mutual TLS.
- Has an asynchronous client that uses only one connection for both sending Commands and receiving Results.
- Supports the server streaming, i.e. a Command can send back multiple Results.
- Provides reconnect and keepalive features.
- Supports the Circuit Breaker pattern.
- Has OpenTelemetry integration.
- Can work with various serialization formats.
- Follows a modular design.
The main cmd-stream-go module contains basic integration tests, while each
submodule (see Architecture) has approximately 90% code
coverage.
See go-client-server-benchmarks for detailed performance comparisons.
Getting started is easy:
- Implement the Command Pattern.
- Use one of the codecs:
- codec-json-go - simple and easy-to-use JSON codec (ideal for prototyping).
- codec-protobuf-go - Protobuf-based codec (requires code generation).
- codec-mus-stream-go - high-performance MUS codec (requires code generation).
Tip: Start with JSON for simplicity, and switch to MUS later for maximum performance.
Here's a minimal end-to-end example showing how Commands can be defined, sent, and executed over the network:
// Calc represents the Receiver (application layer).
// A struct is used instead of an interface for brevity.
type Calc struct{}
func (c Calc) Add(a, b int) int { return a + b }
func (c Calc) Sub(a, b int) int { return a - b }
// AddCmd is a Command that uses Calc to perform addition.
type AddCmd struct {A, B int}
func (c AddCmd) Exec(ctx context.Context, seq core.Seq, at time.Time,
calc Calc, proxy core.Proxy,
) (err error) {
result := CalcResult(calc.Add(c.A, c.B))
_, err = proxy.Send(seq, result) // send result back
return
}
// SubCmd is a Command that uses Calc to perform subtraction.
type SubCmd struct {A, B int}
func (c SubCmd) Exec(ctx context.Context, seq core.Seq, at time.Time,
calc Calc, proxy core.Proxy,
) (err error) {
result := CalcResult(calc.Sub(c.A, c.B))
_, err = proxy.Send(seq, result) // send result back
return
}
// CalcResult is the Result returned by Commands.
type CalcResult int
func (r CalcResult) LastOne() bool { return true }
func main() {
const addr = "127.0.0.1:9000"
var (
invoker = srv.NewInvoker(Calc{})
cmdTypes = []reflect.Type{
reflect.TypeFor[AddCmd](),
reflect.TypeFor[SubCmd](),
}
resultTypes = []reflect.Type{
reflect.TypeFor[CalcResult](),
}
serverCodec = codecjson.NewServerCodec[Calc](cmdTypes, resultTypes)
clientCodec = codecjson.NewClientCodec[Calc](cmdTypes, resultTypes)
)
// Start server.
go func() {
server := cmdstream.MakeServer(serverCodec, invoker)
server.ListenAndServe(addr)
}()
// Make sender.
sender, _ := sndr.Make(addr, clientCodec)
// Send AddCmd.
sum, _ := sender.Send(context.Background(), AddCmd{A: 2, B: 3})
fmt.Println(sum) // Output: 5
// Send SubCmd.
diff, _ := sender.Send(context.Background(), SubCmd{A: 8, B: 4})
fmt.Println(diff) // Output: 4
}The full, runnable example is available in the calc_json.
For further learning, see the resources below.
Built on Go’s standard net package, cmd-stream-go supports
connection-oriented protocols, such as TCP, TLS, and mutual TLS (for client
authentication).
To maximize performance between services:
- Use N parallel connections. More connections typically improve throughput, until a saturation point.
- Pre-establish all connections instead of opening them on-demand.
- Keep connections alive to avoid the overhead from reconnections.
These practices, implemented via the ClientGroup, can significantly enhance throughput and reduce latency between your services.
Already using RPC? You can use cmd-stream-go as a faster transport layer. See
the RPC example.
cmd-stream-go is split into the following submodules:
- core-go: The core module that includes client and server definitions.
- delegate-go: The client delegates all communication-related tasks to its delegate, the server follows the same approach. The connection is also initialized at this level.
- handler-go: The server delegate uses a handler to receive and process Commands.
- transport-go: Responsible for Commands/Results delivery.
- testkit-go: Mocks and test utilities.
cmd-stream-go was designed in such a way that you can easily replace any part
of it.
