Geminio is a comprehensive application-layer network programming library for Go, named after the Doubling Charm from Harry Potter. It provides a unified interface for building network applications with features like RPC, bidirectional RPC, messaging, multi-session management, connection multiplexing, and raw connection handling.
Geminio simplifies network development by abstracting away the complexity of low-level network programming, allowing developers to focus on business logic rather than connection management.
- 🔄 RPC & Bidirectional RPC - Full support for remote procedure calls with bidirectional capabilities
- 📨 Messaging - Reliable message delivery with acknowledgment guarantees
- 🔀 Connection Multiplexing - Multiple logical connections over a single physical connection
- 🆔 Connection Identification - Unique ClientID and StreamID for connection management
- 🔌 Native Compatibility - Seamless integration with Go's
net.Connandnet.Listener - 🔁 High Availability - Built-in automatic reconnection mechanism for clients
- ⚡ High Performance - Optimized for low latency and high throughput
- 🛡️ Production Ready - Extensive testing including stress tests, chaos tests, and performance profiling
- 📦 Zero Dependencies - Lightweight with minimal external dependencies
go get github.com/singchia/geminioServer:
package main
import (
"context"
"log"
"github.com/singchia/geminio/server"
)
func main() {
ln, err := server.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Fatal(err)
}
go func() {
msg, err := end.Receive(context.TODO())
if err != nil {
return
}
log.Printf("Received: %s", string(msg.Data()))
msg.Done()
}()
}
}Client:
package main
import (
"context"
"log"
"github.com/singchia/geminio/client"
)
func main() {
end, err := client.NewEnd("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer end.Close()
msg := end.NewMessage([]byte("Hello, Geminio!"))
if err := end.Publish(context.TODO(), msg); err != nil {
log.Fatal(err)
}
}Geminio follows a layered architecture design:
The library's main abstractions are defined in geminio.go:
// RPC interface
type RPCer interface {
NewRequest(data []byte, opts ...*options.NewRequestOptions) Request
Call(ctx context.Context, method string, req Request, opts ...*options.CallOptions) (Response, error)
CallAsync(ctx context.Context, method string, req Request, ch chan *Call, opts ...*options.CallOptions) (*Call, error)
Register(ctx context.Context, method string, rpc RPC) error
}
// Messaging interface
type Messager interface {
NewMessage(data []byte, opts ...*options.NewMessageOptions) Message
Publish(ctx context.Context, msg Message, opts ...*options.PublishOptions) error
PublishAsync(ctx context.Context, msg Message, ch chan *Publish, opts ...*options.PublishOptions) (*Publish, error)
Receive(ctx context.Context) (Message, error)
}
// Stream interface (combines RPC, Messaging, and Raw connection)
type Stream interface {
RawRPCMessager // RPC + Messaging + net.Conn
StreamID() uint64
ClientID() uint64
Meta() []byte
}
// Multiplexer for managing multiple streams
type Multiplexer interface {
OpenStream(opts ...*options.OpenStreamOptions) (Stream, error)
AcceptStream() (Stream, error)
ListStreams() []Stream
}
// End is the main entry point
type End interface {
Stream // End is also a default stream (streamID = 1)
Multiplexer // End can manage multiple streams
Close()
}Server:
package main
import (
"context"
"log"
"github.com/singchia/geminio/server"
)
func main() {
ln, err := server.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Fatal(err)
}
go func() {
msg, err := end.Receive(context.TODO())
if err != nil {
return
}
log.Printf("Received: %s", string(msg.Data()))
msg.Done()
}()
}
}Client:
package main
import (
"context"
"log"
"github.com/singchia/geminio/client"
)
func main() {
end, err := client.NewEnd("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer end.Close()
msg := end.NewMessage([]byte("hello"))
if err := end.Publish(context.TODO(), msg); err != nil {
log.Fatal(err)
}
}Server:
package main
import (
"context"
"log"
"github.com/singchia/geminio"
"github.com/singchia/geminio/server"
)
func main() {
ln, err := server.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Fatal(err)
}
go func() {
err := end.Register(context.TODO(), "echo", echo)
if err != nil {
log.Fatal(err)
}
}()
}
}
func echo(_ context.Context, req geminio.Request, rsp geminio.Response) {
rsp.SetData(req.Data())
log.Printf("Echo: %s", string(req.Data()))
}Client:
package main
import (
"context"
"log"
"github.com/singchia/geminio/client"
)
func main() {
opt := client.NewEndOptions()
opt.SetWaitRemoteRPCs("echo")
end, err := client.NewEnd("tcp", "127.0.0.1:8080", opt)
if err != nil {
log.Fatal(err)
}
defer end.Close()
rsp, err := end.Call(context.TODO(), "echo", end.NewRequest([]byte("hello")))
if err != nil {
log.Fatal(err)
}
log.Printf("Response: %s", string(rsp.Data()))
}Server:
package main
import (
"context"
"log"
"github.com/singchia/geminio"
"github.com/singchia/geminio/server"
)
func main() {
opt := server.NewEndOptions()
opt.SetWaitRemoteRPCs("client-echo")
opt.SetRegisterLocalRPCs(&geminio.MethodRPC{"server-echo", echo})
ln, err := server.Listen("tcp", "127.0.0.1:8080", opt)
if err != nil {
log.Fatal(err)
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Fatal(err)
}
go func() {
rsp, err := end.Call(context.TODO(), "client-echo", end.NewRequest([]byte("foo")))
if err != nil {
log.Fatal(err)
}
log.Printf("Client echo: %s", string(rsp.Data()))
}()
}
}
func echo(_ context.Context, req geminio.Request, rsp geminio.Response) {
rsp.SetData(req.Data())
log.Printf("Server echo: %s", string(req.Data()))
}Client:
package main
import (
"context"
"log"
"github.com/singchia/geminio"
"github.com/singchia/geminio/client"
)
func main() {
opt := client.NewEndOptions()
opt.SetWaitRemoteRPCs("server-echo")
opt.SetRegisterLocalRPCs(&geminio.MethodRPC{"client-echo", echo})
end, err := client.NewEnd("tcp", "127.0.0.1:8080", opt)
if err != nil {
log.Fatal(err)
}
defer end.Close()
rsp, err := end.Call(context.TODO(), "server-echo", end.NewRequest([]byte("bar")))
if err != nil {
log.Fatal(err)
}
log.Printf("Server echo: %s", string(rsp.Data()))
}
func echo(_ context.Context, req geminio.Request, rsp geminio.Response) {
rsp.SetData(req.Data())
log.Printf("Client echo: %s", string(req.Data()))
}Server:
package main
import (
"log"
"github.com/singchia/geminio/server"
)
func main() {
ln, err := server.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
for {
end, err := ln.AcceptEnd()
if err != nil {
log.Fatal(err)
}
// Open stream #1
sm1, err := end.OpenStream()
if err != nil {
log.Fatal(err)
}
sm1.Write([]byte("hello#1"))
sm1.Close()
// Open stream #2
sm2, err := end.OpenStream()
if err != nil {
log.Fatal(err)
}
sm2.Write([]byte("hello#2"))
sm2.Close()
}
}Client:
package main
import (
"net"
"log"
"github.com/singchia/geminio/client"
)
func main() {
end, err := client.NewEnd("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
defer end.Close()
// End can be used as net.Listener
ln := net.Listener(end)
for {
conn, err := ln.Accept()
if err != nil {
log.Fatal(err)
}
go func(conn net.Conn) {
buf := make([]byte, 128)
n, err := conn.Read(buf)
if err != nil {
return
}
log.Printf("Read: %s", string(buf[:n]))
}(conn)
}
}Check out the examples directory for more comprehensive examples:
- Messager - Message publishing and receiving with acknowledgment
- Message Queue - A simple message queue implementation
- Chatroom - Real-time chatroom example
- Relay - Network relay proxy
- Intranet Penetration - NAT traversal example
Benchmark results (Intel Core i5-6267U @ 2.90GHz):
goos: darwin
goarch: amd64
pkg: github.com/singchia/geminio/test/bench
cpu: Intel(R) Core(TM) i5-6267U CPU @ 2.90GHz
BenchmarkMessage-4 10117 112584 ns/op 1164.21 MB/s 5764 B/op 181 allocs/op
BenchmarkEnd-4 11644 98586 ns/op 1329.52 MB/s 550534 B/op 73 allocs/op
BenchmarkStream-4 12301 96955 ns/op 1351.88 MB/s 550605 B/op 82 allocs/op
BenchmarkRPC-4 6960 165384 ns/op 792.53 MB/s 38381 B/op 187 allocs/op
Geminio is built with a layered architecture:
Contributions are welcome! Please feel free to submit a Pull Request.
- Maintain consistent code style
- Submit one feature at a time
- Include unit tests with your code
- Update documentation as needed
For bug reports or feature requests, please open an issue on GitHub.
Copyright © Austin Zhai, 2023-2030
Licensed under the Apache License 2.0
Made with OSS Insight