Skip to content

A hybrid application-layer networking framework with support for messaging, RPC, bidirectional RPC, multiplexing, and maintaining client-server consistency.

License

Notifications You must be signed in to change notification settings

singchia/geminio

Repository files navigation

A powerful application-layer network programming library for Go

Go Reference Go Report Card License Platform

English | 简体中文


📖 Introduction

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.

✨ Features

  • 🔄 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.Conn and net.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

🚀 Quick Start

Installation

go get github.com/singchia/geminio

Basic Example

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, Geminio!"))
    if err := end.Publish(context.TODO(), msg); err != nil {
        log.Fatal(err)
    }
}

📚 Documentation

Architecture

Geminio follows a layered architecture design:

Core Interfaces

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()
}

💡 Usage Examples

Message Publishing

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)
    }
}

RPC

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()))
}

Bidirectional RPC

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()))
}

Multiplexing

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)
    }
}

📦 More Examples

Check out the examples directory for more comprehensive examples:

⚡ Performance

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

🏗️ Design

Geminio is built with a layered architecture:

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Guidelines

  • 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.

📄 License

Copyright © Austin Zhai, 2023-2030

Licensed under the Apache License 2.0


Activity Trends of singchia/geminio - Last 28 days

Made with OSS Insight

About

A hybrid application-layer networking framework with support for messaging, RPC, bidirectional RPC, multiplexing, and maintaining client-server consistency.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •