Giter Club home page Giter Club logo

Comments (8)

Keith-CY avatar Keith-CY commented on August 29, 2024

Arch & Tech

flowchart
  ioc(IoC container)
  mq(Message Queue)
  actor_0(Actor 0)
  actor_1(Actor 1)
  actor_2(Actor 2)
  actor_n(Actor n)

  ioc -->|spawn| actor_0 & actor_1 & actor_2 & actor_n <-->|pub/sub| mq
Loading

IoC: Inversify
Message Queue: Redis

Inversify and Redis are picked because they are simple and lightweight for the early stage.

from kuai.

Keith-CY avatar Keith-CY commented on August 29, 2024

Interfaces

Actor

As described in Design actor system

Actor State
/*
 * field decorated by @State will be registered as an internal state
 */
@Actor(id: symbol)
class CustomActor extends Actor<CustomState> {
  @State()
  state_a: CustomState['state_a']
}
Actor Behaviours
type Status = ok | error | continue | timeout | stop

class Actor<State, Message> {
  
  /*
   * functions to send messages
   */

  // make a synchronous call
  function call (
    address: ActorAddress,
    message: CustomMessage,
    timeout?: number
  ): {
    status: Status,
    message: CustomMessage,
  }

  // make an asynchronous call
  function cast (
    address: ActorAddress,
    message: CustomMessage,
  ): void

  // send message synchronized to named actors running in specified nodes
  function multi_call (
    nodes: Array<Node>,
    name: string, // registered actor name
    message: CustomMessage,
  ): {
    responses: Array<{
      status: Status,
      message: CustomMessage,
    }>
  }

  // broadcast messages to named actors running in specified nodes
  function abcast (
    nodes: Array<Node>,
    name: string, // registered actor name
    message: CustomMessage,
  ): void

  // reply to the sender in a synchronized call
  function reply (
    client: ActorAddress,
    message: CustomMessage,
  ): void

  /*
   * functions for lifecycle
   */

  // start an actor outside the supervision tree
  function start (
    actorConstructor: ActorConstructor,
    init: CustomState,
    options?: Record<string, string>
  ): {
    status: Status,
    address: ActorAddress,
  }

  // start an actor within the supervision tree
  function startLink (
    actorConstructor: ActorConstructor,
    init: CustomState,
    options?: Record<string, string> 
  ): {
    status: Status,
    address: ActorAddress,
  }

  // stop a actor
  function stop (
    actor: ActorAddress,
    reason: string,
  ): void

}

class CustomActor extends Actor<CustomState, CustomMessage>{

  /*
   * callback behaviors are injected by decorators, and matched by Symbols
   */

  // invoked when the actor is activated
  @Init()
  constructor (
    init: CustomState
  ): { 
    status: Status,
    state: CustomState,
  }

  // invoked when the actor is deactivated
  @Terminate()
  function (
    status: stop,
    reason: string,
    state: CustomState,
  )

  // invoked to handle synchronous call messages, the sender will wait for the response
  @HandleCall(pattern: Symbol)
  function (
    message: CustomMessage,
    from: ActorAddress,
    state: CustomState
  ): {
    status: Status,
    message: CustomMessage,
    state: CustomState,
  }

  // invoked to handle asynchronous call messages, the send won't wait for the response
  @HandleCast(pattern: Symbol)
  function (
    message: CustomMessage,
    state: CustomState,
  ): {
    status: Status,
    state: CustomState,
  }

  // invoked to handle `continue` instruction returned by the previous call
  @HandleContinue()
  function (
    message: CustomMessage,
    state: CustomState,
  }: {
    status: Status,
    state: CustomState,
  }

  // invoked to handle all other unmatched messages
  @HandleInfo()
  function (
    message: any,
    state: CustomState,
  ): {
    status: Status,
    state: CustomState,
  }

  // invoked to inspect internal state of the actor
  @FormatStatus()
  function (
    reason: string,
    state: CustomState,
    context: Context,
  ): void
}

Registry

Use @Actor() decorator to register and instantiate an actor in Registry, an IoC container, implemented based on Inversify

abstract class Registry {
  /*
   * bind an actor into the container
   */
  bind(Actor): symbol

  /*
   * get an actor from the container
   */
  get(Actor): actor

  /*
   * remove Actor from the container
   */
  unbind(Actor): void

  /*
   * create a child registry for hierarchy
   */
  createChild(): Registry
}

Message Router

Simply use the API of Redis Stream

from kuai.

Keith-CY avatar Keith-CY commented on August 29, 2024

Use cases

Create an actor

@Application()
class App {
  actor: Actor // IoC container will instantiate an Actor instance and assign it to `app.actor`

  createActorDynamically(){
    const customActor = Registry.get(CustomActor) // IoC container will instantiate an CustomActor
  }
}

Send a message

@Application()
class App {
  actor: Actor

  send(actor: Actor, message: Message) {
    Actor.call(actor.id, message) // redisClient.xAdd(message.pattern, actor.id, message.body)
  }
}

Handle a message

@Actor()
class CustomActor extends Actor {
  @HandleCall(pattern) // read the message from MQ and matched by this handler
  handlePattern(message: Message, from: ActorAddress, state: State) {
    // business logic
  }
}

Destroy an actor

class App {
  actor: Actor

  terminateActor(actor: Actor) {
    Actor.terminate(actor.id) // send a Symbol('terminate') signal to actor and will be matched by @Terminate()
  }
}

Load configuration/secrets

/*
 * Configuration will be instantiated by IoC container on launch
 */
@Actor()
class Configuration extends Actor {
  @State()
  config: Record<string, string>

  @Init()
  constructor(init: Record<'env', 'develop' | 'test' | 'production'>) {
    const base = this.base()
    const extended = this[env]?.()
    this.config.set({ ...base, ...extended })
  }
}

from kuai.

Keith-CY avatar Keith-CY commented on August 29, 2024

Architecture, interfaces, and use cases have been updated, please have a review @homura @felicityin @yanguoyu @IronLu233 @PainterPuppets

from kuai.

homura avatar homura commented on August 29, 2024
  • maybe a more detailed description of sync Actor::call or Actor::multi_call is helpful, as it is likely to be confused with JS's sync call
  • it might be easier to understand if the use cases are described with some user stories

from kuai.

felicityin avatar felicityin commented on August 29, 2024

How to handle the following situations

  • Message loss
  • The mailbox is full

from kuai.

Keith-CY avatar Keith-CY commented on August 29, 2024
  • maybe a more detailed description of sync Actor::call or Actor::multi_call is helpful, as it is likely to be confused with JS's sync call

As commented in the code snippet, they are simply synchronous methods of sending a message

  /*
   * functions to send messages
   */

  // make a synchronous call
  function call (
    address: ActorAddress,
    message: CustomMessage,
    timeout?: number
  ): {
    status: Status,
    message: CustomMessage,
  }

  // send message synchronized to named actors running in specified nodes
  function multi_call (
    nodes: Array<Node>,
    name: string, // registered actor name
    message: CustomMessage,
  ): {
    responses: Array<{
      status: Status,
      message: CustomMessage,
    }>
  }

If be confused with JS's sync call means Actor::call is too similar to Function.prototype.call(), we can take another method name.

  • it might be easier to understand if the use cases are described with some user stories

It would be a long story to explain them all, can we start with some of them? Pick 2 or 3 methods and I'll add more detailed descriptions

from kuai.

Keith-CY avatar Keith-CY commented on August 29, 2024

How to handle the following situations

  • Message loss
  • The mailbox is full

At this early stage, we will rely on the message queue of Redis for message delivery reliability.

Ref: https://redis.com/solutions/use-cases/messaging/

from kuai.

Related Issues (20)

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.