# rsocket-go: a reactive RSocket implementation for Go

> rsocket-go is a Go implementation of the RSocket protocol with a thin reactive-streams layer, a builder API modelled on the Java SDK and a CLI compatible with rsocket-cli. It ships TCP and WebSocket transports by default and lets you add your own through the core package.

**rsocket/rsocket-go** — rsocket-go implementation

- Repository: https://github.com/rsocket/rsocket-go
- Stars: 521 · Forks: 44
- Language: Go
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/rsocket-rsocket-go

## What RSocket is, and why Go needed its own implementation

rsocket-go is an implementation of the RSocket protocol in Go, and the README lists four things it set out to do.

Design For Golang, Thin reactive-streams implementation, Simulate Java SDK API, and a Fast CLI that is compatible with rsocket-cli.

The third point is the one that tells you most about the design. The Java SDK is the reference implementation of RSocket, and its API is what most people know. Rather than inventing a Go-shaped API, rsocket-go reproduces the Java one, so a developer reading Java RSocket code or documentation can read the Go version and recognise it. The trade is that Go conventions are subordinated to familiarity across the protocol's ecosystem, and the fluent builder style in the Quick Start is not what a Go developer would write from scratch.

The second point, a thin reactive-streams implementation, is the other key decision. RSocket's interaction models are inherently asynchronous and can be long-lived, so the library needs some way to compose them, and the Reactive Streams interface is the portable choice: the same model works whether the source is a channel, a timer, or a network read. The cost is that Reactive Streams in Go is an interface implementation exercise rather than something the language gives you for free, which is why the package carries a rx/ directory with mono and presumably other stream types.

The primary language is Go, the licence is Apache-2.0, the default branch is master, and the recent releases are v0.9.0-alpha+1 from 2026-03-18, v0.8.16 from 2025-10-09 and v0.8.15 from 2025-05-09. The last push was on 2026-08-24.

That version list deserves attention. The 0.x series with an alpha tag means the API is not settled, and the gap between the alpha and the stable line is a decision for you to make rather than the library's default.

The go.mod declares go 1.18 while the README states that the minimal go version is 1.11. The newer figure is what the module actually requires, and the older one is a leftover from the project's early years.

## The builder API: server and client

The Quick Start gives you an echo server and an echo client, and the shape of the API is what matters here.

The server binds a responder and a transport.

```go
func main() {
	err := rsocket.Receive().
		Acceptor(func(ctx context.Context, setup payload.SetupPayload, sendingSocket rsocket.CloseableRSocket) (rsocket.RSocket, error) {
			// bind responder
			return rsocket.NewAbstractSocket(
				rsocket.RequestResponse(func(msg payload.Payload) mono.Mono {
					return mono.Just(msg)
				}),
			), nil
		}).
		Transport(rsocket.TCPServer().SetAddr(":7878").Build()).
		Serve(context.Background())
	log.Fatalln(err)
}
```

The client dials, starts, and then issues a request that blocks for the response.

```go
	// Connect to server
	cli, err := rsocket.Connect().
		SetupPayload(payload.NewString("Hello", "World")).
		Transport(rsocket.TCPClient().SetHostAndPort("127.0.0.1", 7878).Build()).
		Start(context.Background())
	if err != nil {
		panic(err)
	}
	defer cli.Close()
	// Send request
	result, err := cli.RequestResponse(payload.NewString("你好", "世界")).Block(context.Background())
	if err != nil {
		panic(err)
	}
	log.Println("response:", result)
```

Two things are worth noticing. First, the setup payload carries application-level data alongside the protocol handshake, and it is set on the client with SetupPayload before the connection starts. That is the RSocket model's way of letting the server know about the client before the first frame.

Second, the call shape differs between the two interaction models. The responder takes a function returning a mono.Mono, so the server's handler returns a stream of results, while the client calls RequestResponse and then blocks on the returned value with Block(context.Background()). The Block call is the point where the reactive composition becomes a synchronous one, and it is what makes the client example read like ordinary blocking code.

The server is also given two channels in the call chain that are worth reading as a design statement. The Acceptor function receives the setup payload and a sending socket, and returns an RSocket, so the server can respond on the same connection the request came in on. And the responder is bound through NewAbstractSocket, which suggests the socket's interactions are assembled as a set of optional capabilities rather than one fixed behaviour.

## Transports, the core package and your own

The Advanced section is short and points at the thing that makes this library extensible.

rsocket-go provides TCP and Websocket transport implementations by default. Since v0.6.0, you can use the core package to implement your own RSocket transport. The author created an example project showing how to implement an unofficial QUIC transport, and links to rsocket-transport-quic for anyone interested.

That is a meaningful design decision. The protocol is transport-agnostic by design, and RSocket's own framing assumes a reliable, ordered, multiplexed stream, which is a slightly unusual requirement. TCP satisfies it, and WebSocket satisfies it because it runs over TCP, but other protocols such as QUIC need the connection management and stream multiplexing handled in a particular way.

Leaving that to the user is the right call for a protocol library and the wrong call for a library trying to be batteries-included. If you need QUIC, the example project is a working reference rather than a guess.

The dependency list supports the same reading. gorilla/websocket is there for the WebSocket transport, and the go.mod carries no transport framework of its own beyond that.

The repository tree shows the same decomposition at the package level. There is a core/ directory, which is the transport-agnostic part you extend, an internal/ directory, payload/ for the message and setup payload types, rx/ for the reactive streams layer, and then separate packages for balancer/, extension/ and lease/, the last of which covering lease semantics for request-response interactions.

The top-level client.go, server.go, rsocket.go and transporter_builder.go are the public surface, and transporter_builder.go is the file that assembles the transport, which is where you plug a custom implementation in.

One small detail from the go.mod is worth noting for anyone extending the library. The dependency on github.com/jjeffcaii/reactor-go is a separate project providing reactive primitives in Go, which is what the rx/ layer is built on.

## The CLI, the examples and the test story

The README describes a Fast CLI that is compatible with rsocket-cli, and gives both the install command and an example invocation.

```shell
$ go install github.com/rsocket/rsocket-go/cmd/rsocket-cli@latest
```

And the example is a request against a running server.

```shell
rsocket-cli --request -i hello_world --setup setup_me tcp://127.0.0.1:7878
```

The three parts of that command line correspond to the protocol's own vocabulary: a request carrying the payload hello_world, a setup payload carrying setup_me, and a TCP target. That the Go CLI speaks the same command language as the Java one is the compatibility claim made concrete, and it means a mixed-language environment can drive the same server from the same tooling.

The examples directory holds four programs: examples/echo/, examples/echo_bench/, examples/fibonacci/ and examples/word_counter/. The README points to examples/echo/echo.go for more server examples, and the justfile has a target that runs it with garbage collection tracing enabled, which is a reasonable way to watch what the library allocates.

```bash
GODEBUG=gctrace=1 go run ./examples/echo/echo.go
```

The justfile is the development entry point and is short. Build runs go build, lint runs golangci-lint, test runs go test with a coverage profile, test-no-cover and test-race are separate targets, fmt runs go fmt, and cover renders the coverage profile as HTML through go tool cover.

The TODO list in the README is short and tells you the project's status honestly: a Wiki is not written, and the unit test coverage goal of 90 percent is not met. The codecov badge is present in the README, so coverage is tracked even if the target is not reached.

The CI badge points at a Go workflow, and the Go Report Card badge is in the README as well, which means the package is being assessed by the standard Go tooling as well as by its own pipeline.

## Where rsocket-go is the wrong choice

There are three limits, and the first is the most important one for anyone choosing a transport.

The first is that RSocket is not HTTP, and the trade is not always worth it. The protocol gives you cancellation, backpressure, resumability across reconnects and multiplexed streams over one connection, which is genuinely hard to build on a request-response model. But it also means a client that speaks RSocket is a client that has to speak RSocket, and you cannot put one behind a CDN, an HTTP load balancer that does not understand it, or a debugging proxy that only understands HTTP. For a service whose interactions are ordinary request-response calls, that is a large cost for features you will not use.

The second is the API's immaturity. The releases are 0.x, the newest is an alpha, and the README's own TODO shows the wiki is unwritten and the test coverage target is unmet. The builder API is modelled on the Java SDK, which is helpful for cross-language teams and unhelpful for a Go team used to something else. The reactive-streams layer is described as thin, and a thin layer over an interface with backpressure semantics is where the subtle bugs tend to live, particularly around cancellation and around what happens when a subscriber stops reading.

The third is the transport story. TCP and WebSocket are provided and work, but anything else means writing a transport against the core package, following an example project rather than a documented API. The README says the example shows how, which is a starting point and not a specification, and an unofficial transport maintained outside the project is exactly the kind of dependency that ages badly.

The honest alternative depends on which problem you actually have. If you want request-response with cancellation and a single multiplexed connection, rsocket-go is a direct and well-documented answer. If you want streaming server responses to many clients, gRPC with server streaming in Go has a mature ecosystem, generated stubs, and tooling, at the cost of HTTP/2 and the ecosystem's expectations. If you want a message queue between services, using an actual broker is a different design that gives you durability and redelivery, neither of which RSocket provides.

What rsocket-go offers over the alternatives is the specific combination of a Java-compatible API, so a mixed stack can share server implementations and tooling, with Go's concurrency model and a transport layer you can replace. The cost is a 0.x library that tracks an alpha release, whose README admits the documentation and coverage goals are unmet. That is a reasonable dependency for a team that has decided it needs RSocket, and a poor one for a team that has not.

## Conclusion

Adopt rsocket-go if you need RSocket in a Go service, since it gives you the protocol's request-response, fire-and-forget and streaming interaction models with a reactive API rather than a callback wall. Do not adopt it if your requirement is plain request-response over HTTP, because RSocket buys you cancellation and backpressure that a normal HTTP client does not need. Before you commit, note that the newest release is v0.9.0-alpha+1 and the latest stable is v0.8.16, so pin the version rather than tracking the tip.

## FAQ

### What is rsocket-go?

It is a Go implementation of the RSocket protocol, an application protocol for reactive use cases such as request-response, fire-and-forget and streaming interactions. The README lists its goals as designing for Go, a thin reactive-streams implementation, an API that simulates the Java SDK, and a CLI compatible with rsocket-cli.

### How do I install the rsocket-go CLI?

Run go install github.com/rsocket/rsocket-go/cmd/rsocket-cli@latest. The CLI is compatible with rsocket-cli, so an example invocation such as rsocket-cli --request -i hello_world --setup setup_me tcp://127.0.0.1:7878 works the same way as the other RSocket command-line tools.

### Which transports does rsocket-go support and can I add my own?

TCP and WebSocket transports are provided by default. Since v0.6.0 you can use the core package to implement your own RSocket transport, and the README links to an unofficial QUIC transport example project for anyone who needs that.

### How is the rsocket-go API shaped?

It follows the Java SDK. The server is built with rsocket.Receive(), an Acceptor that binds a responder and a transport, and the responder returns a mono.Mono from a RequestResponse function. The client is built with rsocket.Connect(), sets a setup payload and transport, calls Start, and then calls RequestResponse and blocks on the result with Block(context.Background()).

### Is rsocket-go ready for production use?

It is a 0.x library whose newest release is v0.9.0-alpha+1 and whose latest stable is v0.8.16, so you should pin a version. The README's TODO lists the wiki as unwritten and a 90 percent unit test coverage goal as unmet, although coverage is tracked through the codecov badge and the Go workflow.

## Sources

- [Issues](https://github.com/rsocket/rsocket-go/issues)
- [License: Apache-2.0](https://github.com/rsocket/rsocket-go/blob/master/LICENSE)
- [README](https://github.com/rsocket/rsocket-go/blob/master/README.md)
- [Releases](https://github.com/rsocket/rsocket-go/releases)
- [rsocket/rsocket-go on GitHub](https://github.com/rsocket/rsocket-go)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rsocket-rsocket-go
