Giter Club home page Giter Club logo

actr's Introduction

actr

Travis CI Maven Central

Simple actor model implementation for Java

  • Simple API: sending ask and tell messages is just calling class methods
  • POJOs as Actors: focus on business logics, no need to extend any frameworks classes
  • Type safe: no need for instanceof/cast, compile time check prevents sending wrong message to a wrong actor
  • High performance: lock-free implementation and lightweight actor creation

Actor code is guaranteed to be executed in thread-safe context:

  • no concurrent calls for a particular actor (although subsequent calls may be dispatched to different threads)
  • actor state can be safely read/written from actor code without any synchronized/volatile specifiers

Actr's API philosophy is discussed here: https://medium.com/@zakgof/type-safe-actor-model-for-java-7133857a9f72

Schedulers

Schedulers are available to be configured on per-actor or per-actor-system basis.

  • Shared ForkJoinPool scheduler (the default)
    All actors share the common work stealing ForkJoinPool. This option is best for CPU-intensive actors.

  • Thread per actor (pinned thread) scheduler
    Each actor owns a thread and all calls to the actor execute in that dedicated thread. It is useful, in particular, when wrapping non thread safe API. NEW ! JDK's project Loom Virtual Threads are also supported - check this article: https://medium.com/@zakgof/a-simple-benchmark-for-jdk-project-looms-virtual-threads-4f43ef8aeb1

  • Fixed thread pool scheduler
    Uses a pool of a predefined number or threads for scheduling actor calls. It might be beneficial compared to ForkJoinPools for actors involving some io when actor's CPU utilization is not maximum.

  • Scheduler based on a user-provided ExecutorService for a more flexible option.

It's easy to introduce your own fine-tuned scheduler by just implementing IActorScheduler.

Comparison to akka

Akka will require more boilerplate code. Code with Actr will be more concise.

Akka Actr
Type safety No. Sending any message to any actor won't reveal any problems at compile time Type safe
Actor implementation class Must extend AbstractActor No constraints
Message Must create a class for every message type Message type is represented by a method
Message type dispatching Must implement dispatching Message type = method, no need for implementing dispatching
Passing multiple params Must pack into a single message class Message type = method, so supported

Compare the same example implemented with akka and actr. (Note the difference in Printer implementation. With akka, the implementation is 41 lines long with only 1 line of business code (line 34))

Performance

Actr outperforms Akka on common actor operations. A complete opensource benchmark is available here: https://github.com/zakgof/akka-actr-benchmark

Setup

Actr is on Maven Central

Gradle

implementation 'com.github.zakgof:actr:0.4.2'

Maven

<dependency>
  <groupId>com.github.zakgof</groupId>
  <artifactId>actr</artifactId>
  <version>0.4.2/version>
</dependency>

Usage

Having a POJO class

private static class Printer {

    public void print(String s) {
        // This is called in Printer's thread
        System.err.println("[Printer] " + s);
    }
    public String getName() {
        // This is called in Printer's thread
        return "Printer-1";
    }
}

Create an actor

final IActorRef<Printer> printerActor = Actr.newActorSystem("default").actorOf(Printer::new);

Call Printer from another actor

    public void run() {
        // Tell: send text to print
        printerActor.tell(printer -> printer.print("Hello !"));
        
        // Ask: retrieve printer name. Printer#getName runs in Printer's thread, #printerNameReply runs in Caller's thread
        printerActor.ask(Printer::getName, this::printerNameReceived);
    }
    
    private void printerNameReceived(String printerName) {
       // Do smth with Printer's name
       // This is called in Caller's thread
    }
    

More examples

https://github.com/zakgof/actr/tree/master/src/example/java/com/zakgof/actr/example

actr's People

Contributors

oleksandrzakusilo avatar sandared avatar sergey-melnychuk avatar zakgof avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

actr's Issues

Concurrency: `ForkJoinPoolScheduler#schedule(..)` can intermittently lose messages

In ForkJoinPoolScheduler#schedule(..), an actor can intermittently skip messages.

Possible scenarios (others possible):
1.

  • The single actr created;
  • Thread A adds a runnable to the actr's mailbox;
  • Thread A locks the mailbox and runs the runnable;
  • Thread B adds a runnable, the actor's mailbox is still locked, so the 'runnable.run()' is skipped.
  • Thread A unlocks the mailbox;
  • The single actr created;
  • Thread A adds a runnable to the actr's mailbox;
  • Thread A locks the malebox and runs the runnable;
  • Thread B adds a runnable to the same mailbox;
  • Thread A removes the last runnable (from thread B) without execution.

The result: The message (runnable) from thread B is lost.

Support for web api

I really appreciate your idea of pojo classes. It makes it easier to integrate with existing applications.

It is really interesting to have an actor system that can communicate with the external world using standard protocol.

Did you already thing of building module for

  • rest server (spark/jetty integration for example)
  • graphql
  • database persistence actors(redis, sql, mongo, ...)

And example to show how it could be used. What about creating predefined actor/connector to help better understand the power of actr.
Like:

  • PersistenceRedisActr(redis connector with some fonctions),
  • PersistenceHibernateActr(works with hibernate objects)
  • RestApiActr(handling http api request)
  • GraphqlApiActr
  • WebsocketActr
  • EmailActr
  • ...

Each of them with own ExecutorServive and a parameter to increase the number of threads

Concurrency: Unsafe publication in `ActorImpl` constructor

Unsafe publication in ActorImpl constructor.
Instance internals exposed with the inconsistent state to the outer scope.
The 'outer' consumer could see the ActorImpl when its construction is not complete.
Will produce different kinds of 'quantum' effects in the multi-threaded application.

ActorImpl(...) {
    ...
    Actr.setCurrent(this); //Fixme: Unsafe publication!
    ...
    actorSystem.add(this); //Fixme: Unsafe publication!
}

start multi Actorsystem in parallel

here is my code example
a class handling http request with spark java

public class Http {
    void start() {
        Service app = Service.ignite().port(82);
        app.get("/", (req, res)-> {
            System.out.println("request from web: "+Instant.now(Clock.systemUTC()).toString());
            return "hello ..."+new Date().toString();
        });
        System.out.println("start spark server on port :"+app.port());
    }
}

and my main method

 public static void main(String[] args) {
        //final IActorSystem systemBlocking = Actr.newSystem("blocking-System", Schedulers.newThreadPerActorScheduler());
        final IActorSystem system = Actr.newSystem("Example-System");
        final IActorRef<Printer> printerActor1 = system.actorOf(Printer::new, "Printer 1");
        final IActorRef<Printer> printerActor2 = system.actorOf(Printer::new, "Printer 2");
        final IActorRef<Http> server = system.actorOf(Http::new, "Printer 2");
        printerActor1.tell(s -> s.print("first hello"));
        // infinite works
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            printerActor2.tell(s -> s.print("waoh ...."+ Instant.now()));
        }, 1, 5, TimeUnit.SECONDS);
        // start server
        Executors.newFixedThreadPool(5).execute(()-> server.tell(s -> s.start()));
        printerActor2.tell(s -> s.print("second hello"));
        system.shutdownCompletable().join();
        //system.shutdownCompletable().join();
    }

this code is working as expected with this pom

<dependency>
            <groupId>com.github.zakgof</groupId>
            <artifactId>actr</artifactId>
            <version>0.4.0</version>
</dependency>
<dependency>
            <groupId>com.sparkjava</groupId>
            <artifactId>spark-core</artifactId>
            <version>2.9.3</version>
</dependency>

does it make sense ?
how can i start different Actorsystem in parallel ? my ideas was to create a different system for handling http request but i didn't works if i just call shutdownCompletble() twice

Flow: a mailbox can get locked forever

In ForkJoinPoolScheduler#schedule, requires finally block (line 45):

mailbox.locked.set(false);

Otherwise, it can be skipped due to a runtime error, so a mailbox is locked forever.

why do you use Actor Akka

Hi, i am reading your project, why do you choose Akka Actor for working as High concurrency, can u tell me the reason, the pros and cons of Actor Akka, why don't you choose another reactive stream like RxJava, Reactive...

Futures compatibility

First of all, thanks for creating this library. It's great because it's simple (I like simplicity) and TypeSafe (I took a look at akka, but didn't like it so much because its weak typeness). I'm going to use actr in an upcoming project.

I think it would be great to have future compatibility, for instance:

primeNumberDetectorActor.ask(
        /*action*/ primeNumberDetector -> primeNumberDetector.isNumberPrime(5),
        /*consumer*/ isPrime -> System.out.println("5 is prime? "+isPrime));

If I want to handle success / error states, I have to wrap the action in try - catch:

public static class ActorResult<SUCCESS_TYPE>
{
    public SUCCESS_TYPE successValue = null;
    public Throwable error = null;
}

primeNumberDetectorActor.ask(
    /*action*/ primeNumberDetector -> {
        ActorResult<Boolean> result = new ActorResult<>();

        try {
            result.successValue = primeNumberDetector.isNumberPrime(5);
        } catch (Throwable ex) {
            result.error = ex;
        }

        return result;
    },
    /*consumer*/ result -> {
        if (result.error!=null)
            result.error.printStackTrace();
        else System.out.println("5 is prime? "+result.successValue);
    });

It would be nice to have this wrapping done automatically by the library, and to be able to use futures to handle success / error cases.

In the following example, I'm proposing a method with the following signature:

public <ACTOR, SUCCESS_RESULT_TYPE> CompletableFuture<SUCCESS_RESULT_TYPE> askAnsweringFuture(Function<ACTOR, SUCCESS_RESULT_TYPE> action);

Usage:

primeNumberDetectorActor.askAnsweringFuture(primeNumberDetector -> primeNumberDetector.isNumberPrime(5))
                .thenApply(isPrime -> System.out.println("5 is prime? "+isPrime))
                .exceptionally(ex -> ex.printStackTrace());

Since I'm going to use Kotlin, I already made a similar extension method. But since Java doesn't have extension methods, I think this would be a nice feature.

Concurrency: `ForkJoinPoolScheduler#schedule(..)` can intermittently duplicate messages

In ForkJoinPoolScheduler#schedule(..), an actor can intermittently duplicate messages.

A possible scenario (one of ?):

  • The single actr created;
  • Thread A adds a runnable to the actr's mailbox;
  • Thread A locks the mailbox and runs the runnable;
  • Thread B adds a runnable to the same mailbox;
  • Thread A removes the last runnable from thread B (which is lost !!!);
  • The mailbox is still not empty, so the first runnable is executed again;

how can i test it. mvn could not find the jar

after adding this dependency to my mvn project

<dependency>
            <groupId>com.github.zakgof</groupId>
            <artifactId>actr</artifactId>
            <version>0.4.0</version>
</dependency>

i get thie error.

Could not find artifact com.github.zakgof:actr:pom:0.4.0 in central (https://repo.maven.apache.org/maven2)

does anyone has an ideas why. it seems like it is no more awailaible in maven repo

Performance: thread starvation is highly possible in `ForkJoinPoolScheduler#schedule`

When certain single actr or a set of actrs receive messages with a high rate, thread starvation is highly possible.
This will happen in the loop (lines 36-44):

			for(;;) {
				Runnable runnable = mailbox.queue.peek();
				if (runnable != null) {
					...
					runnable.run();
					mailbox.queue.remove();
				} else

When the message rate is high, a certain 'hot' thread wan't 'yield` the execution to others, that can cause a starvation.

In Akka, this is solved with the throughput parameter, that cops the contents of the mailbox into pieces (batches).

REQUEST : Examples of error handling

Hello,
Nice clean implementation of Actors!
Is there an example of Supervisor Actor -> Child Actor error handling?

Scenario:
A Supervisor is sending parsing commands to multiple Child actors to parse a particular piece of data.
If any child fails, the Supervisor should be aware but not escalate, but all the results should be collated to present a say 90% complete solution.
This could be done by wrapping calls etc, but I would like to know if there is a more elegant way of doing that fits this model.

Any tips or suggestions you have that turn out to be implementable, I'll gladly submit as a PR for inclusion.

Thanks, and great work!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.