kameo

repository·main·Indexed 23 days ago

https://github.com/tqwewe/kameo

A high-performance, lightweight Rust library for building fault-tolerant, asynchronous actor-based systems built on Tokio. It supports local concurrency, distributed communication via libp2p, and includes a topic-based message broker with hierarchical routing. The library provides a companion CLI tool, kameo-console, for real-time monitoring of actor systems via a TCP collector.

Tokens
38.9K
Snippets
87
Records
191
Agent score
78%

What's inside kameo

  1. How actor linking works

    main

    Actor linking provides peer-to-peer monitoring between actors, independent of the parent-child supervision hierarchy. This is useful when actors need to monitor each other or when you need cross-node monitoring.

    By default, if a linked actor dies, the linked peer will also stop. However, you can intercept this by implementing the on_link_died method in your Actor implementation. Returning ControlFlow::Continue(()) allows the actor to keep running, while ControlFlow::Break(reason) causes it to stop.

    async fn on_link_died(
        &mut self,
        _actor_ref: WeakActorRef<Self>,
        id: ActorId,
        reason: ActorStopReason,
    ) -> Result<ControlFlow<ActorStopReason>, Self::Error> {
        tracing::warn!("linked actor {id} died: {reason:?}");
        Ok(ControlFlow::Continue(()))  // Keep running
    }
  2. How communication works via Messages

    main
    Communication between actors in Kameo is achieved exclusively through Messages. This ensures loose coupling between components. The messaging system is asynchronous and non-blocking, which allows for efficient communication patterns and responsive application development.
  3. How Kademlia DHT enables decentralized actor lookup

    main

    Kameo uses a Kademlia DHT (Distributed Hash Table) to manage actor registration without a centralized server.

    • Registration: When ActorRef::register is called, the actor's name is stored as a key in the DHT, and the value is a reference to the actor on its host node. This name is then propagated across the network.
    • Lookup: When a node performs a lookup, the DHT retrieves the location of the actor from the network and returns the reference.

    Each node in the swarm stores a portion of the DHT, ensuring efficient discovery across the entire network.

  4. Understand the Actor model in Kameo

    main

    Kameo is built on the Actor model, where the primary building blocks are Actors. An Actor encapsulates its own state and behavior, acting as an independent, concurrent unit.

    Key characteristics of Actors:

    • State Isolation: Actors isolate state, preventing direct access from other parts of the system.
    • Asynchronous Processing: Actors handle messages asynchronously.
    • Fault Tolerance: Because actors are isolated, they can fail and recover without destabilizing the entire system.
  5. When to use bootstrap vs custom swarm configuration

    main

    Use bootstrap() or bootstrap_on() when:

    • You are in development or testing phases.
    • You are prototyping distributed actor logic.
    • You are deploying on local networks where mDNS discovery is sufficient.
    • You want a zero-config setup with sensible defaults.

    Use Custom Swarm Configuration when:

    • You need custom transports (e.g., WebSocket, memory).
    • You require different discovery mechanisms (e.g., custom bootstrap peers instead of mDNS).
    • You need to integrate with an existing libp2p application.
    • You need custom security, authentication, or connection management policies.
    • You are preparing for production deployments where mDNS limitations (local network only) are a factor.
  6. Understand Kameo's concurrency and runtime model

    main

    Kameo leverages the Tokio runtime to manage actor execution:

    • Async Nature: Actors are asynchronous, allowing many actors to run on a single thread efficiently, which is ideal for IO-bound tasks. Non-blocking network operations can proceed without stalling other actors.
    • Parallelism: By enabling the rt-multi-thread feature in the Tokio runtime, Kameo can utilize multiple CPU cores to distribute actors and handle parallel workloads.
    • Sequential Processing: Within a single actor, messages are processed sequentially. This ensures state changes occur in a well-defined order, maintaining consistency and correctness.
  7. How supervision trees work in Kameo

    main

    Supervision trees establish parent-child relationships where a supervisor actor automatically manages the lifecycle of its children. If a child actor fails (via panic, error, or exit), the supervisor uses a configured SupervisionStrategy to decide whether to restart the child, other siblings, or all children. This allows for building resilient, self-healing systems following the Erlang/OTP model.

    impl Actor for MySupervisor {
        // ...
        fn supervision_strategy() -> SupervisionStrategy {
            SupervisionStrategy::OneForOne
        }
        // ...
    }
  8. How actors work in Kameo

    main

    Actors are the core abstraction in Kameo, encapsulating state and behavior. They communicate asynchronously via message passing, which enables high concurrency and scalability.

    Key Components

    • ActorRef: A reference returned when an actor is spawned. Use this to send messages to the actor.
    • Mailbox: A queue where incoming messages are stored before processing. Actors can use bounded mailboxes (to apply backpressure) or unbounded mailboxes.
    • Messaging: Messages are sent asynchronously and processed sequentially by the receiving actor.
    • Supervision: Actors can supervise other actors. Using lifecycle hooks like on_panic and on_link_died, a supervisor can implement hierarchical error handling and recovery strategies.

    Lifecycle Hooks

    Implementing the Actor trait allows you to intercept the actor's lifecycle:

    • on_start: Called before the actor starts processing messages. Used for initialization.
    • on_stop: Called when an actor is explicitly stopped or all ActorRef handles are dropped. Used for cleanup.
    • on_panic: Invoked when an actor panics or encounters an error during message processing.
    • on_link_died: Called when a linked actor dies, allowing the actor to react to failures in related actors.
  9. Manage failures with Supervision

    main
    Kameo employs a Supervision strategy based on the "let it crash" principle. Actors can be organized into supervision trees, where parent actors monitor their children. When a child actor fails, the parent is responsible for responding according to a defined strategy, providing a structured approach to error handling and system resilience.
  10. Understand Kameo's networking and distributed capabilities

    main

    Kameo is designed for distributed systems and uses libp2p for networking.

    Key networking concepts:

    • Actor Registration & Lookup: Uses Kademlia Distributed Hash Table (DHT).
    • Routing: Messages are routed using multiaddresses, supporting protocols like TCP/IP and QUIC.
    • Remote Communication: Actors communicate across nodes via RemoteActorRef. Unlike gRPC, Kameo does not require predefined schemas or code generation for cross-node communication, allowing for more dynamic interactions.
  11. Handle actor responses with Replies

    main
    A Reply is the response sent back to complete a request cycle. To define custom reply types for your application, you must implement the Reply trait. This allows actors to exchange structured data and status information, enabling robust interaction where actors depend on the outcomes of their requests.
  12. Requirements for remote messaging: RemoteActor and #[remote_message]

    main

    To enable communication between nodes, you must satisfy two requirements:

    1. Implement RemoteActor: The actor must implement the RemoteActor trait to uniquely identify the actor type for routing.
    2. Annotate with #[remote_message]: Every message handler must be annotated with the #[remote_message] macro. This macro assigns unique identifiers to the actor and message type, allowing the system to deserialize incoming messages without a centralized enum. The macro uses the linkme crate to build a registry of message handlers at link time.

    Note: The UUID string assigned to each message must be unique within the crate to avoid conflicts.

    #[derive(RemoteActor)]
    pub struct MyActor;
    
    #[remote_message]
    impl Message<Inc> for MyActor {
        type Reply = i64;
    
        async fn handle(&mut self, msg: Inc, _ctx: &mut Context<Self, Self::Reply>) -> Self::Reply {
            self.count += msg.amount as i64;
            self.count
        }
    }