Comments (8)
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
IoC: Inversify
Message Queue: Redis
Inversify and Redis are picked because they are simple and lightweight for the early stage.
from kuai.
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.
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.
Architecture, interfaces, and use cases have been updated, please have a review @homura @felicityin @yanguoyu @IronLu233 @PainterPuppets
from kuai.
- maybe a more detailed description of sync
Actor::call
orActor::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.
How to handle the following situations
- Message loss
- The mailbox is full
from kuai.
- maybe a more detailed description of sync
Actor::call
orActor::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.
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)
- Optimization about Model Pattern
- Release v0.0.1-alpha HOT 4
- Version numbers in mvp and project template are not covered by lerna HOT 6
- Versions in lock file are not bumped correctly HOT 1
- `env-paths` is required in `@ckb-js/kuai-common` but not specified in package.json HOT 2
- Support constructing custom transactions based on outputs of store models
- Technical breakdown of sudt demo HOT 11
- Technical breakdown of sudt demo backend design HOT 15
- Technical breakdown of sudt demo frontend design HOT 8
- Alias `pattern` as `filter` HOT 1
- Simplify usage of `Lock` decorator
- More useful ideas HOT 3
- Connect sudt management to Neuron by wallet connect HOT 3
- Failed to create new contract in the new project created by kuai init HOT 2
- Add a middleware to transfer the response for default response struct.
- Some problem in developing the sUDT manager HOT 3
- Maybe we need to add states for data HOT 2
- Categorize documents
- Kuai Usage Summary From Acceptance Team HOT 4
- Technical design of account calibration
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from kuai.