diggsey / act-zero Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
Hey here is how I'm exploring it right now, let me know what you think.
Main function creates the supervision tree root and starts its children:
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
env_logger::init();
let cluster_manager = spawn_actor(ClusterManager::new().await);
let _ = call!(cluster_manager.start_children()).await?;
Ok(())
}
On root create, save self Addr and creates children and saves their addrs as well
#[async_trait]
impl Actor for ClusterManager {
async fn started(&mut self, pid: Addr<Self>) -> ActorResult<()> {
self.pid = pid.downgrade();
self.app_manager_pid = spawn_actor(AppManager::new(self).await);
self.build_manager_pid = spawn_actor(BuildManager::new(self).await);
Produces::ok(())
}
}
Root start children function
async fn start_children(&mut self) -> ActorResult<Vec<()>> {
use futures::stream::FuturesUnordered;
use futures::StreamExt;
let children = FuturesUnordered::new();
children.push(call!(self.build_manager_pid.start_children()));
children.push(call!(self.app_manager_pid.start_children()));
// some weird nested async wrangling goes on here
let ran: Vec<_> = children.collect().await;
let filtered: Vec<()> = ran.into_iter().flatten().flatten().collect();
Produces::ok(filtered)
}
Child actor also has children of its own
//BuildManager
pub async fn start_children(&self) -> ActorResult<Vec<()>> {
use futures::stream::FuturesUnordered;
info!("Starting build manager");
let bm_worker = spawn_actor(BuildMessageWorker::new(self).await);
let incoming_worker = spawn_actor(IncomingMessageWorker::new(self).await);
let children = FuturesUnordered::new();
children.push(call!(bm_worker.listen()));
children.push(call!(incoming_worker.listen()));
let ran: Vec<ActorCallResult<()>> = children.collect().await;
let filtered = ran.into_iter().filter_map(Result::ok).collect();
Produces::ok(filtered)
}
The ClusterManager
BuildManager
and AppManager
actors need to stick around for ever always listening to (and handling) new messages coming from outside the system (through NATS).
Hey here is how I'm exploring it right now, let me know what you think.
Main function, supervision tree root:
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
env_logger::init();
let cluster_manager_struct = ClusterManager::new().await;
let cluster_manager = spawn_actor(cluster_manager_struct.clone());
let _ = call!(cluster_manager.start_children()).await?;
Ok(())
}
Starts children on startup
#[async_trait]
impl Actor for ClusterManager {
async fn started(&mut self, pid: Addr<Self>) -> ActorResult<()> {
self.pid = pid.downgrade();
self.app_manager_pid = spawn_actor(AppManager::new(self).await);
self.build_manager_pid = spawn_actor(BuildManager::new(self).await);
Produces::ok(())
}
}
Root start children function
async fn start_children(&mut self) -> ActorResult<Vec<()>> {
use futures::stream::FuturesUnordered;
use futures::StreamExt;
let children = FuturesUnordered::new();
children.push(call!(self.build_manager_pid.start_children()));
children.push(call!(self.app_manager_pid.start_children()));
let ran: Vec<_> = children.collect().await;
let filtered: Vec<()> = ran.into_iter().flatten().flatten().collect();
Produces::ok(filtered)
}
Child actor also has children of its own
//BuildManager
pub async fn start_children(&self) -> ActorResult<Vec<()>> {
use futures::stream::FuturesUnordered;
info!("Starting build manager");
let bm_worker = spawn_actor(BuildMessageWorker::new(self).await);
let incoming_worker = spawn_actor(IncomingMessageWorker::new(self).await);
let children = FuturesUnordered::new();
children.push(call!(bm_worker.listen()));
children.push(call!(incoming_worker.listen()));
let ran: Vec<ActorCallResult<()>> = children.collect().await;
let filtered = ran.into_iter().filter_map(Result::ok).collect();
Produces::ok(filtered)
}
Firstly many thanks for act-zero - it's very nice to work with. I've converted a program to it from Riker.
I'm trying to run some cleanup in my actor's drop()
, but I can't figure a way to wait for it to complete. .termination()
returns immediately. I guess the channel is closed before the inner actor is dropped?
I guess maybe the AddrInner could drop its Actor value
before closing channels, but I'm not sure where. Any ideas would be appreciated.
// Copy of using_async_std example
use act_zero::runtimes::async_std::spawn_actor;
use act_zero::*;
struct HelloWorldActor;
impl Actor for HelloWorldActor {}
impl HelloWorldActor {
async fn say_hello(&mut self) {
println!("Hello, world!");
}
}
impl Drop for HelloWorldActor {
fn drop(&mut self) {
println!("actor drop starts, waiting a sec");
std::thread::sleep(std::time::Duration::from_secs(1));
println!("actor drop done");
}
}
#[async_std::main]
async fn main() -> Result<(), ActorError> {
let addr = spawn_actor(HelloWorldActor);
call!(addr.say_hello()).await?;
let ended = addr.termination();
std::mem::drop(addr);
ended.await;
println!("The End");
Ok(())
% cargo run --example using_async_std --features=async-std
Hello, world!
actor drop starts, waiting a sec
The End
%
I attempted to do it but ran into problems that I don't currently have the skills to tackle: #8
I enjoyed playing with act-zero and will like to start working with it as a replacement for actix.
I will like to know the following:
I eagerly waiting for feedback on this, thanks
Hey since we can use async in traits since Rust 1.75, would it possible to now remove async_trait package?
I know it would be needed still to use dynamic dispatch. But i'm wondering if its possible to use it as a fallback?
Would it be possible to pass in the tracing span into send and call so it can create proper traces of calls between actors?
Is there currently a way to kill or stop the actor?
Here is what I tried so far:
struct HelloWorldActor;
impl Actor for HelloWorldActor {}
impl HelloWorldActor {
async fn say_hello(&mut self) {
loop {
println!("Hello, world!");
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
env_logger::init();
let addr = spawn_actor(HelloWorldActor);
send!(addr.say_hello());
drop(addr);
Ok(())
}
The current the document of send!
said: "The method must take &mut self
as the receiver."
I tried the method likes this async fn get_count(&self) -> ActorResult<usize>
, it pass the compler.
Then I found the following words at https://old.reddit.com/r/rust/comments/hu10wh/actzero_asyncawait_compatible_actor_system/
Actor methods can have &self, &mut self or self: Addr<Local> receiver types, and this controls whether these methods can execute concurrently or not.
How about putting these words into the document of send!
?
Hi, thanks for sharing such an amazing project.
I am interested in Actor and currently use actix. I am in search for a more flexible crate and I find act-zero amazing. I tried your tokio example but rust could not find tokio runtime.
use act_zero::runtimes::tokio::spawn_actor;
| ^^^^^ could not find `tokio` in `runtimes`
I am fairly new to rust, please help me out with possible solutions.
Thank you
Hi Diggsey!
Thanks for sharing this interesting project. I like the async/await support and your emphasis on correctly mutating actor state.
I have a small application currently implemented in Actix that I'd like to explore reimplementing in act-zero. Rather than try to tackle the whole thing I thought I would make a few little programs in act-zero demonstrating how I commonly use actors. I'm not super experienced with async rust(or sync rust) so I thought I could get some feedback or maybe we could turn anything interesting into some act-zero examples?
actix-web integration
The first example integrates actix-web and act-zero. In particular I wanted to send a message to an actor and create an http response from the actor's response. For the example I made an actor that tracks the number of times a route is hit. Another route returns the count.;
This was pretty straightforward: https://github.com/dmm/act-zero-examples/blob/main/actix-web-interop/src/main.rs
The only tricky part was using app_data() instead of data() to prevent the Addr<> from being wrapping in an Arc.
Some more things I'd like to attempt:
Feel free to close this if you don't want it cluttering your issues.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.