Skip to content

sllt/rain

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rain Framework

Go Report Card GoDoc License: MIT

Rain is a powerful, high-performance actor-based framework for Go that brings Erlang/OTP-style concurrency and fault tolerance to the Go ecosystem. Built with network transparency and distributed computing in mind, Rain enables you to create scalable, fault-tolerant, and highly available applications with ease.

🌟 Key Features

🌐 Network Transparency

  • Seamless Distribution: All network interactions are hidden behind Erlang-like messaging APIs
  • Location Independence: Processes can communicate regardless of their physical location
  • Automatic Discovery: Built-in service discovery and node clustering capabilities
  • Protocol Agnostic: Support for TCP, TLS, and custom transport protocols

πŸ›‘οΈ Fault Tolerance

  • Supervisor Trees: Hierarchical error handling with configurable restart strategies
  • Let It Crash Philosophy: Isolated failures don't bring down the entire system
  • Process Isolation: Each actor runs in its own lightweight process
  • Graceful Degradation: System continues operating even when components fail

⚑ High Performance

  • Lightweight Processes: ~1KB memory footprint per process (vs ~2KB in Erlang)
  • Fast Startup: ~1ms process spawn time
  • Efficient Messaging: Lock-free MPSC queues for inter-process communication
  • Compression Support: Built-in message compression for network efficiency
  • Zero-Copy Operations: Optimized memory management for high throughput

πŸ“ˆ Scalability

  • Horizontal Scaling: Easy distribution across multiple nodes
  • Millions of Processes: Handle massive concurrent workloads
  • Dynamic Load Balancing: Automatic work distribution
  • Resource Management: Built-in backpressure and flow control

πŸ”§ Developer Experience

  • Zero Dependencies: No external libraries required
  • Rich Ecosystem: Built-in web server, database connectors, and more
  • Hot Code Reloading: Update code without stopping the system
  • Comprehensive Monitoring: Built-in metrics and observability tools

πŸš€ Quick Start

Installation

go mod init your-project
go get github.com/sllt/rain

Basic Example

Here's a simple example demonstrating the core concepts:

package main

import (
    "fmt"
    "time"

    "github.com/sllt/rain"
    "github.com/sllt/rain/act"
    "github.com/sllt/rain/gen"
)

func main() {
    // Create applications
    apps := []gen.ApplicationBehavior{
        &DemoApp{},
    }

    // Start node with applications
    node, err := rain.StartNode("demo@localhost", apps, gen.NodeOptions{})
    if err != nil {
        panic(err)
    }

    // Wait for the node to terminate
    node.Wait()
}

// Application definition
type DemoApp struct {
    act.Application
}

func (da *DemoApp) Load(args ...any) (gen.ApplicationSpec, error) {
    return gen.ApplicationSpec{
        Name:        "DemoApp",
        Description: "Demo Rain Framework application",
        Version:     gen.NewVersion("DemoApp", "1.0.0"),
        Children: []gen.ApplicationChildSpec{
            {
                Child: &DemoActor{},
                Name:  "demo_actor",
            },
        },
    }, nil
}

func (da *DemoApp) Start(mode gen.ApplicationMode) {}
func (da *DemoApp) Terminate(reason error)         {}

// Actor definition
type DemoActor struct {
    act.Actor
    counter int
}

func (da *DemoActor) Init(args ...any) error {
    fmt.Println("🎭 Demo actor started")
    
    // Send a message to self every second
    da.SendAfter(da.PID(), "tick", time.Second)
    return nil
}

func (da *DemoActor) HandleMessage(from gen.PID, message any) error {
    switch msg := message.(type) {
    case string:
        if msg == "tick" {
            da.counter++
            fmt.Printf("⏰ Tick #%d received from %s\n", da.counter, from)
            
            // Schedule next tick
            da.SendAfter(da.PID(), "tick", time.Second)
        } else {
            fmt.Printf("πŸ“¨ Message received: %s from %s\n", msg, from)
        }
    default:
        fmt.Printf("πŸ“¦ Unknown message: %#v from %s\n", message, from)
    }
    return nil
}

func (da *DemoActor) Terminate(reason error) {
    fmt.Printf("πŸ’€ Demo actor terminated: %s\n", reason)
}

πŸ“š Core Concepts

Actors

Actors are lightweight, isolated processes that communicate through message passing:

type MyActor struct {
    act.Actor
    state map[string]any
}

func (a *MyActor) HandleMessage(from gen.PID, message any) error {
    switch msg := message.(type) {
    case GetRequest:
        value := a.state[msg.Key]
        a.Send(from, GetResponse{Value: value})
    case SetRequest:
        a.state[msg.Key] = msg.Value
        a.Send(from, SetResponse{Success: true})
    }
    return nil
}

Supervisors

Supervisors manage actor lifecycles and handle failures:

type MySupervisor struct {
    act.Supervisor
}

func (s *MySupervisor) Init(args ...any) (act.SupervisorSpec, error) {
    return act.SupervisorSpec{
        Children: []act.SupervisorChildSpec{
            {
                Name:  "worker1",
                Child: &WorkerActor{},
                Args:  []any{"worker1_config"},
            },
            {
                Name:  "worker2", 
                Child: &WorkerActor{},
                Args:  []any{"worker2_config"},
            },
        },
        Strategy: act.SupervisorStrategyOneForOne,
        Restart:  act.SupervisorRestartPermanent,
    }, nil
}

Applications

Applications provide structure and lifecycle management:

type MyApp struct {
    act.Application
}

func (app *MyApp) Load(args ...any) (gen.ApplicationSpec, error) {
    return gen.ApplicationSpec{
        Name:        "MyApp",
        Description: "My awesome application",
        Version:     gen.NewVersion("MyApp", "1.0.0"),
        Children: []gen.ApplicationChildSpec{
            {
                Child: &MySupervisor{},
                Name:  "main_supervisor",
            },
        },
    }, nil
}

🌐 Distributed Computing

Node Clustering

// Start multiple nodes
node1, _ := rain.StartNode("node1@192.168.1.10", apps, gen.NodeOptions{
    Network: gen.NetworkOptions{
        Cookie: "secret_cookie",
    },
})

node2, _ := rain.StartNode("node2@192.168.1.11", apps, gen.NodeOptions{
    Network: gen.NetworkOptions{
        Cookie: "secret_cookie",
    },
})

// Connect nodes
node1.Connect("node2@192.168.1.11")

Remote Process Communication

// Send message to remote process
remotePID := gen.PID{
    Node: "node2@192.168.1.11",
    ID:   "remote_actor",
}
actor.Send(remotePID, "Hello from remote!")

// Spawn process on remote node
pid, err := actor.SpawnRegister("node2@192.168.1.11", "remote_worker", &WorkerActor{}, "worker_args")

πŸ”§ Advanced Features

Web Server Integration

// Built-in web server
webSpec := meta.WebServerSpec{
    Host: "localhost",
    Port: 8080,
    Handler: &MyWebHandler{},
}

pid, err := node.Spawn("web_server", gen.MetaWebServer, webSpec)

Database Integration

// Database connection pooling
dbSpec := meta.DatabaseSpec{
    Driver: "postgres",
    DSN:    "postgres://user:pass@localhost/db",
    Pool:   meta.PoolOptions{MaxConns: 10},
}

pid, err := node.Spawn("db_pool", gen.MetaDatabase, dbSpec)

Monitoring and Metrics

// Built-in metrics collection
metrics := node.Metrics()
fmt.Printf("Processes: %d\n", metrics.ProcessCount)
fmt.Printf("Messages/sec: %.2f\n", metrics.MessageRate)
fmt.Printf("Memory usage: %d MB\n", metrics.MemoryUsage/1024/1024)

πŸ“– Documentation

πŸ§ͺ Testing

Rain provides comprehensive testing utilities:

# Run unit tests
go test ./...

# Run with race detection
go test -race ./...

# Run benchmarks
go test -bench=. ./...

# Test with coverage
go test -cover ./...

Testing Actors

func TestMyActor(t *testing.T) {
    // Create test environment
    env := unit.NewTestEnv(t)
    defer env.Stop()

    // Spawn actor
    pid, err := env.Spawn("test_actor", &MyActor{})
    require.NoError(t, err)

    // Send test message
    env.Send(pid, TestMessage{Data: "test"})

    // Verify response
    response := env.Receive()
    assert.Equal(t, "expected_response", response)
}

πŸ—οΈ Architecture

System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Rain Node                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚ Application β”‚  β”‚ Application β”‚  β”‚ Application β”‚         β”‚
β”‚  β”‚     A       β”‚  β”‚     B       β”‚  β”‚     C       β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚ Supervisor  β”‚  β”‚ Supervisor  β”‚  β”‚ Supervisor  β”‚         β”‚
β”‚  β”‚    Tree     β”‚  β”‚    Tree     β”‚  β”‚    Tree     β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚   Actor     β”‚  β”‚   Actor     β”‚  β”‚   Actor     β”‚         β”‚
β”‚  β”‚ Processes   β”‚  β”‚ Processes   β”‚  β”‚ Processes   β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                    Core Runtime                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚  Scheduler  β”‚  β”‚  Network    β”‚  β”‚  Registry   β”‚         β”‚
β”‚  β”‚             β”‚  β”‚   Layer     β”‚  β”‚             β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Message Flow

Actor A                    Actor B                    Actor C
   β”‚                          β”‚                          β”‚
   β”‚ ──── Send Message ────►  β”‚                          β”‚
   β”‚                          β”‚ ── Forward Message ──►  β”‚
   β”‚                          β”‚                          β”‚
   β”‚ ◄──── Response ──────────│ ◄──── Response ─────────│
   β”‚                          β”‚                          β”‚

πŸ”§ Configuration

Node Configuration

nodeOptions := gen.NodeOptions{
    // Network configuration
    Network: gen.NetworkOptions{
        Cookie:      "secure_cookie",
        MaxNodes:    100,
        Compression: true,
        TLS: gen.TLSOptions{
            CertFile: "cert.pem",
            KeyFile:  "key.pem",
        },
    },

    // Process configuration
    Process: gen.ProcessOptions{
        MailboxSize:     1000,
        MaxMessageSize:  1024 * 1024, // 1MB
        SpawnTimeout:    time.Second * 5,
    },

    // Logging configuration
    Log: gen.LogOptions{
        Level:  gen.LogLevelInfo,
        Format: gen.LogFormatJSON,
        Output: os.Stdout,
    },

    // Resource limits
    Limits: gen.LimitsOptions{
        MaxProcesses: 1000000,
        MaxMemory:    8 * 1024 * 1024 * 1024, // 8GB
    },
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages