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.
- 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
- 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
- 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
- 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
- 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
go mod init your-project
go get github.com/sllt/rainHere'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)
}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 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 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
}// 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")// 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")// Built-in web server
webSpec := meta.WebServerSpec{
Host: "localhost",
Port: 8080,
Handler: &MyWebHandler{},
}
pid, err := node.Spawn("web_server", gen.MetaWebServer, webSpec)// 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)// 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)- Getting Started Guide - Step-by-step tutorial
- Actor Model - Understanding actors and message passing
- Supervision Trees - Fault tolerance patterns
- Distributed Systems - Building distributed applications
- Performance Tuning - Optimization techniques
- API Reference - Complete API documentation
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 ./...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)
}βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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 β β β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Actor A Actor B Actor C
β β β
β ββββ Send Message βββββΊ β β
β β ββ Forward Message βββΊ β
β β β
β βββββ Response βββββββββββ βββββ Response ββββββββββ
β β β
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
},
}